JVM(类加载、运行时数据区、堆内存、方法区、本地接口、执行引擎和垃圾回收)java虚拟机(JVM)的超详细知识点

JVM虚拟机

  • 一、JVM的概述
    • 1.为什么要学习JVM
    • 2.虚拟机
    • 3.JVM的作用
      • 作用
      • 特点
    • 4.JVM的位置
    • 5.JVM的分类
    • 6.各个组成部分的用途
    • 7.Java 代码的执行流程
    • 8.JVM 架构模型
  • 二、JVM 结构-类加载
    • 1. 类加载子系统
    • 2.类加载的角色
    • 3.类加载过程
      • 加载
      • 连接
      • 初始化
    • 4.类加载器
    • 5.双亲委派机制
    • 6.类的主动使用/被动使用
  • 三、JVM 运行时数据区
    • 运行时数据区的概念和组成
      • 1.程序计数器
      • 2.java虚拟机栈
      • 3.本地方法栈
      • 4.java堆内存 *
      • 5.方法区
  • 四、本地方法接口
    • 1.什么是本地方法
    • 2.为什么要使用 Native Method
  • 五、执行引擎
    • 1.概述
    • 2.什么是解释器,什么是JIT编译器
    • 3.为什么Java是半编译半解释型语言
  • 六、垃圾回收
    • 1.垃圾回收的概述
      • 什么是垃圾?
      • 为什么需要 GC?
      • 早期垃圾回收
      • 垃圾回收机制
    • 2.垃圾回收的相关算法
      • 3.垃圾回收中的相关概念

一、JVM的概述

1.为什么要学习JVM

    JVM是java底层代码的运行机制,虽然我们不学习jvm,也可以写出漂亮的代码,但是在一些面试过程中,不懂JVM会被面试官虐的体无完肤。一切知识点都是为了面试而准备,当然,这也是作为一个中高级程序员的必修之课。学会了JVM,才能清楚项目管理和性能调优的基本原理。

2.虚拟机

    1.所谓虚拟机(Virtual Machine),就是一台虚拟的计算机。它是一款软件,用来执 行一系列虚拟计算机指令。大体上,虚拟机可以分为系统虚拟机和程序虚拟机。
    2.大名鼎鼎的 VMware 就属于系统虚拟机,它是完全对物理计算机的仿真,提供了一 个可运行完整操作系统的软件平台。程序虚拟机典型的代表就是 java 虚拟机了,它专门为 执行某个单个计算机程序而设计。在 java 虚拟机中执行的指令我们称为 java 字节码指令。
    3.Java 虚拟机是一种执行 java 字节码文件的虚拟计算机,它拥有独立的运行机制。
    4.Java 技术的核心就是 java 虚拟机,因为所有的 java 程序都运行在 java 虚拟机内部。

3.JVM的作用

作用

    我们都知道JVM是java代码的运行环境,他是JDK不可或缺的一部分,而java虚拟机就是二进制字节码的运行环境,负责装在字节码到内部。,解释/编译为对应平台上的机器码指令执行。对每一条 java 指令,java 虚拟机中都有详细定义。如怎么取操作数,怎么处理操作数,处理结果放在哪儿
JVM(类加载、运行时数据区、堆内存、方法区、本地接口、执行引擎和垃圾回收)java虚拟机(JVM)的超详细知识点_第1张图片

特点

  1. 一次编译,到处运行
  2. 自动内存管理
  3. 自动垃圾回收功能

JVM(类加载、运行时数据区、堆内存、方法区、本地接口、执行引擎和垃圾回收)java虚拟机(JVM)的超详细知识点_第2张图片
现在的 JVM 不仅可以执行 java 字节码文件,还可以执行其他语言编译后的字节码文件,是一 个跨语言平台。

4.JVM的位置

JVM(类加载、运行时数据区、堆内存、方法区、本地接口、执行引擎和垃圾回收)java虚拟机(JVM)的超详细知识点_第3张图片
JVM 是运行在操作系统之上的,它与硬件没有直接的交互。
JVM(类加载、运行时数据区、堆内存、方法区、本地接口、执行引擎和垃圾回收)java虚拟机(JVM)的超详细知识点_第4张图片

5.JVM的分类

  1. 类加载器(ClassLoader)
  2. 运行时数据区(Runtime Data Area)
  3. 执行引擎(Execution Engine)
  4. 本地库接口(Native Interface)

简图:
JVM(类加载、运行时数据区、堆内存、方法区、本地接口、执行引擎和垃圾回收)java虚拟机(JVM)的超详细知识点_第5张图片
详细图:
JVM(类加载、运行时数据区、堆内存、方法区、本地接口、执行引擎和垃圾回收)java虚拟机(JVM)的超详细知识点_第6张图片

6.各个组成部分的用途

程序在执行之前先要把 java 代码转换成字节码(class 文件),jvm 首先需要把字节码通过一定的方式 类加载器(ClassLoader) 把文件加载到内存中 的运行时数据区(Runtime Data Area),而字节码文件是 jvm 的一套指 令集规范,并不能直接交个底层操作系统去执行,因此需要特定的命令解析器将字节码翻译成底层系统指令再交由 CPU 去执行,而这个过程中需要调用其他语言的接口来实现整个程序的功能,这就是这 4 个主要组成部分的职责与功能
而我们通常所说的 JVM 组成指的是运行时数据区(Runtime Data Area),因为通常需要程序员调试分析的区域就是“运行时数据区”,或者 更具体的来说就是“运行时数据区”里面的 Heap(堆)模块。

7.Java 代码的执行流程

JVM(类加载、运行时数据区、堆内存、方法区、本地接口、执行引擎和垃圾回收)java虚拟机(JVM)的超详细知识点_第7张图片
JVM(类加载、运行时数据区、堆内存、方法区、本地接口、执行引擎和垃圾回收)java虚拟机(JVM)的超详细知识点_第8张图片
JVM 主要任务就是负责将字节码装载到其内部,解释/编译为对应平台上的机器指令执行。JVM 使用类加载器(Class Loader)装载 class 文件。 类加载完成后,会进行字节码校验,字节码校验通过之后 JVM 解释器会把字节 码翻译成机器码交由操作系统执行。

8.JVM 架构模型

    Java 编译器输入的指令流基本上是一种基于栈的指令集架构,另一种指令集架构 是基于寄存器的指令集架构.

    这两种架构之间的区别:
基于栈式架构的特点

  1. 设计和实现更简单,适用于资源受限的系统.
  2. 使用零地址指令方式分配,其执行过程依赖于操作栈,指令集更小,编译器容易实现
  3. 不需要硬件支持,可移植性好,更好实现跨平台.

基于寄存器式架构特点:

  1. 指令完全依赖于硬件,可移植性差.
  2. 性能优秀,执行更高效.
  3. 完成一项操作使用的指令更少.

JVM(类加载、运行时数据区、堆内存、方法区、本地接口、执行引擎和垃圾回收)java虚拟机(JVM)的超详细知识点_第9张图片
使用 javap -v class 文件可以将 class 文件反编译为指令集. 所以由于跨平台的设计,Java 指令集都是根据栈来设计的,不同 CPU 架构不同, 所以不能设计为基于寄存器的. 优点是跨平台,指令集小,编译器容易实现. 缺点是性能下降,实现同样功能需要更多的指令.

二、JVM 结构-类加载

1. 类加载子系统

JVM(类加载、运行时数据区、堆内存、方法区、本地接口、执行引擎和垃圾回收)java虚拟机(JVM)的超详细知识点_第10张图片
类加载器子系统负责从文件系统或者网络中加载 class 文件。

2.类加载的角色

JVM(类加载、运行时数据区、堆内存、方法区、本地接口、执行引擎和垃圾回收)java虚拟机(JVM)的超详细知识点_第11张图片

  1. class file 存在于硬盘上,可以理解为设计师画在纸上的模板,而最终这个模板在执行的时候是要加载 JVM 当中来,根据这个模板实例化出 n 个一模一样的实例.
  2. class file 加载到 JVM 中,被称为 DNA 元数据模板,放在方法区中.
  3. 在.class–>JVM–>最终称为元数据模板,此过程就要有一个运输工具(类加 载器 Class Loader),扮演一个快递员的角色.

3.类加载过程

JVM(类加载、运行时数据区、堆内存、方法区、本地接口、执行引擎和垃圾回收)java虚拟机(JVM)的超详细知识点_第12张图片

加载

  1. 根据类的地址,从硬盘上读取类的信息,
  2. 将信息读入到方法区,生成Class类的对象

连接

    验证: 验证字节码文件格式是否是当前虚拟机所支持的文件格式,语法格式
    准备: 为静态成员分配默认值(int 默认值0) 注意static final在编译期间赋值
    解析: 将字节码中符号引用 替换成 直接引用

初始化

  类在什么时候开始初始化?

  1. )创建类的实例,也就是 new 一个对象
  2. 访问某个类或接口的静态变量,或者对该静态变量赋值
  3. 调用类的静态方法
  4. 反射(Class.forName(“”))
  5. 初始化一个类的子类(会首先初始化子类的父类)

  类初始化的数据

  1. 先初始化静态的,多个静态的按照从上向下的顺序执行,
  2. 如果类有父类,则先初始化父类的静态,然后是子类.
  3. 如果是创建对象,先调用父类的构造方法,然后是子类自己的构造方法

JVM(类加载、运行时数据区、堆内存、方法区、本地接口、执行引擎和垃圾回收)java虚拟机(JVM)的超详细知识点_第13张图片
JVM(类加载、运行时数据区、堆内存、方法区、本地接口、执行引擎和垃圾回收)java虚拟机(JVM)的超详细知识点_第14张图片

4.类加载器

从JVM来说,类加载器可以分为两种

  1. 启动类加载器(不是java语言写的)
  2. 其他所有类加载器(都是java语言写的,且全部继承自抽象类java.lang.ClassLoader.)
    JVM(类加载、运行时数据区、堆内存、方法区、本地接口、执行引擎和垃圾回收)java虚拟机(JVM)的超详细知识点_第15张图片

站在开发者的角度:
启动类加载器(引导类加加载器)
    这个类加载器使用 C/C++语言实现,嵌套在 JVM 内部.它用来加载 java 核心类库。负责加载扩展类加载器和应用类加载器。加载\lib
扩展类加载类器
     是由java语言实现的 继承自ClassLoader,负责加载 D:\ProgramFiles\Java\jdk1.8.0_261\jre\lib\ext
应用程序类加载器(系统类加载器)
    Java编写,sun.misc.Launcher$AppClassLoader 实现,派生于 ClassLoader 类。 负责加载用户类
用户自定义类加载器
    例如tomcat

5.双亲委派机制

Java 虚拟机对class文件采用的是按需加载的方式, 要该类时才会将它的class文件加载到内存中生成class对象。而且加载某个类的class文件时,Java 虚拟机采用的是双亲委派模式,即把请求交由父类处理,它是一种任务委派模式.
JVM(类加载、运行时数据区、堆内存、方法区、本地接口、执行引擎和垃圾回收)java虚拟机(JVM)的超详细知识点_第16张图片
工作原理:

  1. 如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请 求委托给父类的加载器去执行.
  2. 如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器.
  3. 如果父类加载器可以完成类的加载任务,就成功返回,倘若父类加载器无法完 成加载任务,子加载器才会尝试自己去加载,这就是双亲委派机制.
  4. 如果均加载失败,就会抛出 ClassNotFoundException 异常。

目的: 为了安全考虑 避免了用户自己写的类覆盖了系统中的类.
为什么要用到双亲委派机制?比如我们建一个java.lang包,里面class一个String类,我们加载类时,加载的是直接的类还是JDK的类呢?
JVM(类加载、运行时数据区、堆内存、方法区、本地接口、执行引擎和垃圾回收)java虚拟机(JVM)的超详细知识点_第17张图片
那当然是不行的,如果可以运行,那么自己写的类将会替换掉JDK的String
JVM(类加载、运行时数据区、堆内存、方法区、本地接口、执行引擎和垃圾回收)java虚拟机(JVM)的超详细知识点_第18张图片
双亲委派优点

  1. 安全,可避免用户自己编写的类动态替换 Java 的核心类,如 java.lang.String
  2. 避免全限定命名的类重复加载(使用了 findLoadClass()判断当前类是否已加 载)

6.类的主动使用/被动使用

JVM 规定,每个类或者接口被首次主动使用时才对其进行初始化,有主动使用,自然就有被动使用.
    主动使用:

  1. 通过new关键字被导致类的初始化
  2. 访问类的静态变量、静态方法
  3. 对某个类进行反射操作
  4. 初始化子类,父类也会被初始化
  5. 执行main方法

    被动使用:

  1. 仅仅使用类的静态常量 而且是直接赋字面量的那种,比如
    public final static int NUMBER = 5 ; //不会导致类初始化,被动使用
    public final static int RANDOM = new Random().nextInt() ; //会导致类的初 始化,主动使用
  2. 构造某个类的数组时不会导致该类的初始化
    比如:Student[] students = new Student[10] ;

主动使用和被动使用的区别在于类是否会被初始化.

三、JVM 运行时数据区

运行时数据区的概念和组成

JVM 的运行时数据区,不同虚拟机实现可能略微有所不同,但都会遵从Java 虚拟机规范,Java 8 虚拟机规范规定,Java 虚拟机所管理的内存将会包括以下几 个运行时数据区域: 程序计数器、java虚拟机栈、本地方法栈、java堆内存和方法区
JVM(类加载、运行时数据区、堆内存、方法区、本地接口、执行引擎和垃圾回收)java虚拟机(JVM)的超详细知识点_第19张图片
Java 虚拟机定义了序运行期间会使用到的运行数据区,其中有一些会随着虚拟 机启动而创建,随着虚拟机退出而销毁.另外一些则是与线程一一对应的.这些与线程对应的区域会随着线程开始和结束而创建销毁

1.程序计数器

 jvm中的程序计数器不是cpu中的寄存器,  可以理解为计数器.

 是一块非常小的内存空间,运行速度是最快的,不会出现内存溢出情况.

 作用:记录当前线程中的方法执行的位置. 以便于cpu在切换执行时,记录程序执行的为位置.

 在运行时数据区中唯一一个不会出现内存溢出的区域.

2.java虚拟机栈

     背景: java为了移植性好(跨平台) 所以将运行程序的设计架构为栈结构运行, 而不是依赖于cpu的寄存器架构。

  • 栈是运行时的单位(加载方法运行),
  • 而堆是存储的单位(存储对象的).

作用运行方法 一个方法就是一个栈帧. 栈帧中包含( 局部变量(基本类型,引用地址) 方法地址,返回地址)
     栈中的操作: 入栈,出栈
     栈中异常 StackOverflowError: 线程请求的栈深度大于虚拟机所允许的深度。 递归调用方法次数过多
     栈的运行原理: 第一个方法被加载 入栈 在方法中调用了其他方法, 另一个方法入栈 方法运行结束后出栈.
     栈帧的结构:

  • 局部变量表:

  • 操作数栈

  • 动态链接

  • 方法返回地址

3.本地方法栈

 当我们在程序中调用本地方法时,会将本地方法加载到 本地方法栈中执行.
 
 也是线程私有的, 如果空间不够,也会出现栈溢出错误.   hashCode();

4.java堆内存 *

     概述: 堆是JVM内存中核心的区域,用来存储创建出来的对象,是线程共享的。堆空间在jvm启动时被创建,大小可以设置。 物理上不是连续的,逻辑上是连续的空间。堆中会发生垃圾回收.
     堆内存区域划分:
     新生代(新生区)
          新生代分为:

    伊甸园区(新生成的对象储存)
    幸存者0
    幸存者1

     老年代(老年区)

    为什么要分区?
    把不同的生命周期的对象存储在不同区域,这样不同的区域可以使用不同的垃圾回收算法。可以提高垃圾回收的效率.

    对象在堆内存中的的过程:
    新建的对象 存放在伊甸园区, 第一次垃圾回收时,垃圾对象直接被回收掉, 存活下来的对象,会把他存放到幸存者0/幸存者1.
    再次垃圾回收时,把在幸存者0区 存活的对象 移动到幸存者1区,然后将幸存者0区清空,依次交替执行。每次保证有一个幸存者区域是空的,内存是完整的.
    当对象经过15次垃圾回收后,依然存活的.将被移动到老年区(老年区,垃圾回收的频率就比较低)

    堆各区域的占比
新生代 占整堆的三分之一
新生代中的 伊甸园区 幸存者0 幸存者1 占比是 8:1:1
对象经过15次垃圾回收后,依然存活的.将被移动到老年区
为什么最大是15次 , 因为在对象头中只有4个bit位的空间,只能表示最大值15.

    堆空间的参数设置
一般所说JVM优化 就是调整jvm相关各区的参数
https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html

    分代收集思想 Minor GC、Major GC、Full GC
一般情况下收集新生代 Minor GC/Yong GC
老年代 会触发Major GC / Old GC
整堆收集 Full GC
    整堆收集触发的条件:
 System.gc();时
 老年区空间不足
 方法区空间不足
 开发期间尽量避免整堆收集 ( 在垃圾回收时 会STW stop the world 回收时停止其他线程运行 )

    TLAB 机制
TLAB 线程本地分配缓存区
在多线程情况下,可以在堆空间中通过-XX:UseTLAB 设置. 在堆空间中为线程开辟一块空间,用来存储线程中产生的一些对象, 避免空间竞争,提高分配效率

     字符串常量池位置
jdk7之前,将字符串常量池位置在 方法区(永久代)中存储. jdk8之后 方法区又称为元空间
jdk8之后,将字符串常量池的位置 放到了堆空间. 因为方法区只有触发FUll GC时才会回收.
因为程序中大量的需要使用字符串,所以将字符串常量池的位置改变到了堆中,可以及时回收无效的字符串常量.

5.方法区

    概述: 方法区也是一块内存空间,逻辑上属于堆,为了区分,称为元空间(jdk8之后),主要用来存储类的信息。在jvm启动时创建 大小可以分配。如果加载的类太多,也会报内存溢出错误,是线程共享的.

 方法区的大小可以通过 -XX:MetaspaceSize 设置
 方法区在windows中默认大小是21MB
 如果到达21MB会触发FULL GC
 可以将其值设置的大一些,减少FULL GC的触发
 方法区中主要回收运行时常量池,类的信息

类的信息卸载(回收) 条件是比较苛刻的,满足3个条件:

  1. 该类以及子类的对象没有被引用

  2. 该类的类加载器被卸载

  3. 该类的LClass对象也没有被引用

四、本地方法接口

1.什么是本地方法

    简单来讲,一个 Native Method 就是一个 java 调用非 java 代码的接口。该方法的底层实现由非 Java 语言实现, 比如 C、C++。

2.为什么要使用 Native Method

    我们的java程序 需要与外部(计算机硬件)进行数据交互( 例如hashCode read() start() ) 。可以直接调用外部 的本地方法实现。JVM解释是用C写的,可以更好的与本地方法交互

五、执行引擎

1.概述

  1. 执行引擎是 Java 虚拟机核心的组成部分之一。

  2. JVM 的主要任务是负责装载字节码到其内部,但字节码并不能够直接运行在 操作系统之上

  3. 如果想要让一个 Java 程序运行起来,执行引擎(Execution Engine) 的任务就是将字节码指令解释/编译为对应平台上的本地机器指令才可以。

     前端编译(  .java   --->  .class)
    
     字节码  不等于  机器码
    
     需要jvm将字节码加载到内存中.
    
     需要通过执行引擎将字节码 解释/编译成机器码     后端编译(  .class  ---> 机器码)
    

2.什么是解释器,什么是JIT编译器

     解释器: 将字节码逐行解释执行
     JIT编译器(即时编译器): 将字节码整体编译为机器码执行

3.为什么Java是半编译半解释型语言

因为JVM执行引擎为半解释型,半编译型。因为:

  1. 逐行解释执行效率低.
  2. JVM会针对使用频率较高的热点代码进行编译,并缓存起来. 执行效率提高.
  3. 编译是需要消耗时间的。所以jvm刚刚启动后,可以先通过 解释器 解释执行代码。之后再使用编译器编译执行. 两种结合在一起.

六、垃圾回收

1.垃圾回收的概述

JVM(类加载、运行时数据区、堆内存、方法区、本地接口、执行引擎和垃圾回收)java虚拟机(JVM)的超详细知识点_第20张图片

  1. Java 和 C++语言的区别,就在于垃圾收集技术和内存动态分配上,C++语 言没有垃圾收集技术,需要程序员手动的收集。
  2. .垃圾收集,不是 Java 语言的伴生产物。早在 1960 年,第一门开始使用内存 动态分配和垃圾收集技术的 Lisp 语言诞生。
  3. 关于垃圾收集有三个经典问题:哪些内存需要回收? 什么时候回收? 如何回收?
  4. 垃圾收集机制是 Java 的招牌能力,极大地提高了开发效率。

总结: 垃圾收集机制并不是java语言首创的,但是又是java的招牌,java可以自动垃圾回收。

什么是垃圾?

垃圾是指在运行程序中没有任何引用指向的对象,这个对象就是需要被回收的垃圾。

为什么需要 GC?

垃圾如果不及时清理,越积越多,可能会导致内存溢出。
垃圾多了,内存碎片较多 例如数组 需要连续空间

早期垃圾回收

     在早期的 C/C++时代,垃圾回收基本上是手工进行的。开发人员可以使用 new 关键字进行内存申请,并使用 delete 关键字进行内存释放。比如以下代码:

MibBridge *pBridge= new cmBaseGroupBridge();
//如果注册失败,使用 Delete 释放该对象所占内存区域 
if(pBridge->Register(kDestroy)!=NO ERROR) 
   delete pBridge;

     这种方式可以灵活控制内存释放的时间,但是会给开发人员带来频繁申请和释放 内存的管理负担。倘若有一处内存区间由于程序员编码的问题忘记被回收,那么 就会产生内存泄漏,垃圾对象永远无法被清除,随着系统运行时间的不断增长, 垃圾对象所耗内存可能持续上升,直到出现内存溢出并造成应用程序崩溃。

    有了垃圾回收机制后,上述代码极有可能变成这样

MibBridge *pBridge=new cmBaseGroupBridge();
    pBridge->Register(kDestroy);

    java语言是自动垃圾收集的

垃圾回收机制

    自动内存管理: 无需开发人员手动参与内存的分配与回收,这样降低内存泄漏和内存溢出的风险。以更专心地专注于业务开发。

    自动收集的担忧: 自动回收方便了程序员的开发,但是降低处理内存问题的能力。自动虽好,但是还是应该了解并掌握一些相关内存管理的知识.

Java 堆是垃圾收集器的工作重点

  1. 频繁收集 Young 区

  2. 较少收集 Old 区

  3. 基本不收集元空间(方法区)

2.垃圾回收的相关算法

    内存溢出与内存泄漏
    溢出:内存不够用了
    泄露:有些对象已经在程序不被使用了,但是垃圾回收机制并不能判定其为垃圾对象,不能将其回收掉。 这样的对象越积越多, 长久也会导致内存不够用.
    例如: 与数据库连接完之后,需要关闭连接通道,但是没有关闭。
             io 读写完成后没有关闭

垃圾收集算法分为两大类:
1.垃圾标记阶段算法
    主要是来判定哪些对象已经不再被使用,标记为垃圾对象。判定对象为垃圾的标准: 不被任何引用所指向的对象. Object obj = new Object();

    垃圾回收阶段的算法:
(1)引用计数算法(在jvm中不被使用)。如果有一个引用指向此对象,那么计数器加1. 如果没有引用指向,计数器为0, 此时就判定为垃圾.
    优点: 方便使用,设计简洁。
    缺点: 增加了计数器的存储空间,计数需要消耗时间。导致一个循环引用问题. 好几个对象之间相互引用,但是没有其他引用指向他们,此时垃圾回收不能回收他们,但是也没有引用指向. 这就造成了内存泄漏。

 Object obj = new Object();

​        obj=null;

(2)可达性分析算法 / 根搜素算法(这是java目前所使用的垃圾标记算法):从一些活跃引用(GCRoots 根)开始, 如果对象被根直接或间接引用,那么此对象不是垃圾, 否则标记为垃圾对象。解决 了循环引用问题,设计简单 ,运行高效,防止内存泄漏。那么又一个问题,哪些引用被用来当做根呢?
JVM(类加载、运行时数据区、堆内存、方法区、本地接口、执行引擎和垃圾回收)java虚拟机(JVM)的超详细知识点_第21张图片

    可以本当做跟的引用:

  1. 虚拟机栈中引用的对象 (方法中引用的对象)
  2. 本地方法栈中引用的对象
  3. 静态变量所引用的对象
  4. 常量引用指向的对象
  5. 被synchronized当做锁的对象
  6. Java 虚拟机内部的引用

总结: 栈中引用的(正在使用的) 方法区,常量池中(生命周期较长的),被synchronized当做锁的对象

finalize() 方法机制
    java允许对象在销毁前去调用finalize(),去处理一些逻辑. 一般不用(不建议用)。不要自己显示的去调用finalize()方法,在里面写代码一定要慎重。 在 finalize()时可能会导致对象复活。 finalize()由垃圾回收器调用,没有固定的时间。一个糟糕的 finalize()会严重影响 GC 的性能。比如 finalize 是个死循环。
    对象状态:

  1. 可触及的:从根节点开始,可以到达这个对象 。 (没有被标记为垃圾)
  2. 可复活的:对象的所有引用都被释放,但是对象有可能在 finalize()中复活。 确定为垃圾了,但没有调用finalize()方法.
  3. 不可触及的:对象的 finalize()被调用,并且没有复活,那么就会进入不可触及 状态。不可触及的对象不可能被复活,因为 finalize()只会被调用一次.

2.垃圾回收阶段算法

(1)标记-清除算法
分为两个阶段:

  1. 标记: 标记出从根可达的对象,标记的是被引用的对象.
  2. 清除: 此清除并非直接将垃圾对象清除掉, 而是将垃圾对象的地址维护到一个空闲列表中。之后如果有新的对象产生,判断空闲列表中的对象空间能否存放得下新的对象,如果能放得下,那么就覆盖垃 圾对象.

优点: 简单,容易理解
缺点: 效率低, 会产生STW(在回收时,停止整个应用程序), 会产生内存碎片.
(2)复制算法
将内存分为大小相等的两块,每次只使用其中的一块儿区域即可。 当回收时,将不是垃圾的对象,复制到另一块内存中,排放整齐。 然后将原来的内存块清空,减少内存碎片。

优点:运行高效,减少内存碎片
缺点:用到两倍的内存空间 ,对于G1垃圾回收器,将每个区域又可以拆分成更多的小区域,需要维护各区之间的关系.在新生代中的幸存者0和幸存者1这两个区域使用复制算法.
(3)标记压缩算法
背景: 复制算法需要移动对象位置,移动的数量如果多的情况下,效率低. 对于新生代来讲还是不错的. 对于老年代,大量的对象是存活的. 如果需要移动就比较麻烦效率低.
实现: 将存活对象标记出来,重新在本内存空间中排放位置,清除其他空间的垃圾对象.

标记-清除 和 标记-压缩对比 标记清除是不移动对象, 不会把垃圾对象清除掉(维护在一个空闲列表中); 标记-压缩是要移动对象的. 要清除掉垃圾对象.

优点: 不会像标记-清除算法那样会产生内存碎片
缺点: 效率相对低, 对象位置移动后需要重新设置对象地址, 也会有STW

3.垃圾回收中的相关概念

System.gc() 的理解 调用System.gc()方法,会触发FULL GC(整堆收集), 但是不一定调用后会立刻生效。因为垃圾回收是自动的。一般情况下,不要在项目中显示的去调用.

Stop the World Stop the World -->STW 在垃圾回收时,会导致整个应用程序停止。 在标记垃圾对象时,需要以某个时间节点上内存中的情况进行分析(拍照 快照) 因为不进行停顿的话,内存中的对象不停的变化,导致分析结果不准确。 停顿是不可避免的,优秀的垃圾回收器尽可能减少停顿的时间.

对象引用 Object obj = new Object(); 就是将对象分等级: 强引用(有引用指向的对象) 软引用 弱引用 虚引用(都是垃圾了)

  1. 强引用:Object obj = new Object(); obj引用创建的对象 那么此对象就是被强引用的。 这种情况下,即使内存不够用了,报内存溢出,也不会回收。
  2. 软引用:当内存足够使用时,先不回收这类对象,当虚拟机内存不够用时,要回收此类对象.
  3. 弱引用: 此类对象只能生存到下次垃圾回收时, 只要发生垃圾回收,就会回收此类对象.
  4. 虚引用:发现即回收.

你可能感兴趣的:(JVM,JavaSE基础,java,JVM,类加载,堆)