JVM相关问题
- 一 、JDK、JRE、JVM
- 二、内存管理
- 三、GC如何判断对象可以被回收(这是JVM的基础)
一 、JDK、JRE、JVM
- JDK:Java Development Kit【Java开发工具】,提供给Java开发人员来使用的。
- JRE:Java Runtime Environment【Java运行时环境】,提供给需要运行Java程序的用户来使用的。
- JVM:Java Virtual Machine【虚拟机】,将Java编译后的字节码文件.class,解释成机器代码
- .java文件通过 “JDK” 的 “java工具” 的 “javac”,编译成.class文件
- .class文件在不同操作系统的JVM上,调用lib(类库),将.class文件解释成对应操作系统的机器码
二、内存管理
- 对象内存管理介绍
- 编译好的java程序需要运行在JVM中;
- 程序,无论是代码还是数据,都要存储在内存中;
- JVM为java程序提供并管理所需要的内存空间;
- JVM内存分为“堆”、“方法区”、“栈”、“本地方法栈”、“程序计数器”,其中堆和方法区是线程共有的;栈、本地方法栈和程序计数器是线程私有的。
- 堆
- 这部分空间用于存储使用new关键字所创建的对象。
- 访问对象需要依靠引用变量,当一个对象没有任何引用时,被视为废弃的对象,属于被回收的范围,该对象中所有成员变量也随之被回收。所以成员变量的生命周期为:从对象在堆中创建开始到对象从堆中被回收结束
- 栈
- 这部分空间用于存储程序运行时在方法中声明的所有局部变量。例如:main()方法中有如下代码
- 一个运行的Java程序从开始创建到结束会有多次方法的调用;JVM会为每一个方法的调用在栈中分配一个对应空间,这个空间称为该方法的栈帧;一个栈帧对应一个正在调用中的方法,栈帧中存储了该方法的参数、局部变量等数据,当某个方法调用完成后,其对应的栈帧将被清楚,局部变量失效。
- 方法区
- 存放类的所有信息(方法和变量)。java程序运行时,首先会通过类加载器载入类文件的字节码信息,经过解析后将其装入方法区。
- 当类的信息被加载到方法区时,除了类的类型信息外,同时类内的方法定义也被加载到方法区,类在实例化对象时,多个对象会拥有各自在堆中的空间,但所有实例对象是共用在方法区中一份方法定义的,同时也是同用一个方法区的Class。
- 本地方法栈
- 本地方法栈与虚拟机的作用相似,不同之处在于虚拟机栈为虚拟机执行的Java方法服务,而本地方法栈则为虚拟机使用到的Native方法。有的虚拟机直接把本地方法栈和虚拟机栈合二为一。
- 会抛出stackOverflowError和OutOfMemoryError异常
三、GC如何判断对象可以被回收(这是JVM的基础)
- 判断方法
- 引用计数法:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收
- 可达性分析法:从GC Roots开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的,那么虚拟机就判断是可回收对象
- 引用计数法的特殊情况
可能会出现A引用了B,B又引用了A,这时候就算他们都不再使用了,但因为相互引用,计数器=1,永远无法被回收
- GC Roots的对象有
- 虚拟机栈中的引用对象(即new出来的对象)
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中JNI引用的对象
- 可达性算法
- 可达性算法中的不可达对象并不是立即死亡的,对象拥有一次自我拯救的机会:第一次是经过可达性分析发生没有与GC Roots相连接的引用链,第二次是在由虚拟机自动建立的Finalizer队列中判断是否需要执行finalize()方法
- 当对象编程(GC Roots)不可达时,GC会判断该对象是否执行过finalize方法,若执行了则直接回收。否则,若对象未执行过finalized方法,将其放入F-Queue队列,由低优先级线程执行该队列中对象的finalize方法。执行finalize方法完毕后,GC会再次判断该对象是否可达,若不可达,则进行回收,否则对象复活。
- 每个对象只能触发一次finalize()方法
- 由于finalize()方法运行代价高昂,不确定性大,无法保证各个对象的调用顺序,不推荐大家使用