1.前言
1.1java技术体系
JVM是JDK中最重要的组成部分,不同版本的JDK底层有基于对应操作系统的特定java虚拟机实现
1.2JVM整体结构
跨平台:JVM来生成不同操作系统的机器码从而实现跨平台
2.JVM的组成
例:当执行javac Math.java后生成 Math.class,然后执行java Math.class时,类装载子系统会将字节码文件装载到JVM的运行时数据区;然后字节码执行引擎会进行程序的执行。由此可以看出java虚拟机由3部分组成:
- 类装载子系统
- 运行时数据区
- 字节码执行引擎
2.1栈内存详解
栈(线程):存放局部变量(不只是局部变量);每有一个线程运行时JVM就会分配一个栈空间来为线程分配专属内存
栈帧:栈的内部是由一系列栈帧组成;例如当运行main方法时,jvm会为其分配一个专属的内存区域;而不同的方法中有各自的局部变量,并且这些局部变量的作用域只在方法中有效。JVM为了区分不同局部变量的运行范围,会在每个方法运行时为这个方法在整个线程栈上面分配专属于这个方法的内存区域(存放方法内部的局部变量);这个方法对应的专属内存区域叫做方法对应的栈帧区域。
数据结构中的栈 和 JVM内存中的栈:名称叫做栈是因为程序运行时的方法嵌套调用顺序应该遵循后调用先销毁内存。正好是后进先出的栈数据结构。
操作数栈:进行运行操作时所需的临时内存空间
程序计数器:存放当前线程正在执行的代码的位置或行号;总程序计数器这一大块内存会为每个线程单独分配一个线程专属的小程序计数器。程序计数器的目的是为了多线程时线程挂起后找到上次执行的位置。它是由字节码执行引擎进行修改的。
方法出口:当前方法运行结束后要记录上层调用者方法的现场,例如:通过方法出口来知道回到main方法后要继续执行哪行代码。
局部变量表:栈中的局部变量通过引用来指向堆
方法区(元空间):常量+静态变量+类信息 (例子中的math.class就是被类装载子系统装载到方法区)
本地方法栈:当执行native方法时找到C或C++实现的方法,为其分配内存
分析class文件了解JVM真正执行过程:javap -c 命令对class进行反汇编,得到可读的文件
2.2堆内存详解
java中堆中分为年轻代和老年代两大部分,内存空间比例为1:2;年轻代又分为Eden区、survivor区(s0区,s1区)
程序进行new关键词创建对象时会在Eden区进行分配,当Eden区内存不够时 字节码执行引擎会执行minor gc操作。
GC Roots根节点:线程栈的本地变量、静态变量、本地方法栈的变量等。
可达性分析算法会从GC Roots出发,找到它引用的对象,一直找下去直到没有引用对象为止,这个链条上的对象为非垃圾对象。JVM会直接将这些对象复制到survivor区中去。(不在链条上的对象直接清除掉);当Eden元区又满时,不光会回收Eden区,还会回收非空的s0区。(回收过程一样)s0区中还被引用着的对象会被放入s1区中,剩下的s0区对象直接清除。
分代年龄:当对象没经历一次GC且没被清除掉,它的分代年龄就会+1。当分代年龄超过15会直接被JVM放入老年代中。静态变量引入的对象、线程池、对象池、缓存等等都可能会被放入老年代中。
minor GC:当Eden区满时字节码执行引擎会触发minor gc,minor gc只对年轻代内存做GC处理。
full GC:当老年代满时,字节码执行引擎会进行full gc操作,对整个堆进行GC处理。
阿里深度的面试题:JVM做full gc时为什么要stop the word呢??
java虚拟机调优目的是减少full gc,执行full gc时会"stop the word"会停止所有用户线程!!垃圾回收完毕后再恢复全部的用户线程。
如果full gc时不做stop the word,full gc找完对象链条后 ;同时程序会继续执行,有可能gc还未结束时线程就已经结束了,这时会释放所有的局部变量的内存空间,这时之前full gc找的链条就不正确了,很多标记为非垃圾的对象又变为垃圾了!!!