学习目标:
- 掌握JVM内存模型
- 读懂字节码
- 什么是STW,为什么要STW。
学习内容:
JVM整体结构及内存模型
JVM虚拟机包括4个部分:
1、类加载器子系统(Class Loader):负责从硬盘上加载字节码文件。
2、运行时数据区(Runtime Data Area):存储运行时数据,包含方法区,堆,栈,本地方法栈,程序计数器。
3、字节码执行引擎(Execution Engine):负责将字节码解释/编译为真正的机器码。
4、本地方法接口(Native Interface):负责调用操作系统本地方法。
运行时数据区:
1、程序计数器:是一块较小的空间,是当前线程所执行的字节码的行号指示器。
2、栈:描述的是Java方法执行的内存模型,每个方法在执行的同时会创建一个线帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息,每个方法从调用直至执行完成的过程都对应着一个线桢在虚拟机栈中入栈出栈的过程。
3、本地方法栈:与虚拟机栈一样,虚拟机栈是服务Java方法的,本地方法栈是为虚拟机调用native方法服务的。
4、Java堆:是Java虚拟机中内存最大的一块,是被所有线程共享的,在虚拟机启动时候创建,Java堆唯一的目的就是存放对象实例,机会所有对象实例都在这里分配内存。
5、方法区:储存已被虚拟机加载的类信息、常量、静态资源、即时编译后的代码等数据。方法区是很重要的系统资源,是硬盘和CPU的中间桥梁,承载着操作系统和应用程序的实时运行。
补充:
堆和方法区是共享的,程序计数器,栈,和本地方法栈是每个线程独立的。
程序计数器的作用:多线程的时候,线程可能会挂起暂停,程序计数器负责记录下当前线程正在执行的Java方法的JVM指令地址,等线程唤醒后继续执行。
堆:1、多个线程共享Java堆,另外可以划分线程私有缓冲区。2、一个JVM实例只存在一个堆内存,堆是Java内存管理的核心区域。在JVM启动时,Java堆内存就被创建了,其空间大小也确定了,是JVM管理的最大一块内存空间。3、堆内存的空间是可以调节的,例如:-Xms:10m(堆起始大小),一般情况下将起始值和最大值设置一样,会减少垃圾回收之后堆内存重新分配大小的次数,提高效率。4、堆处于物理上不连续的内存空间中,但逻辑上应该被视为连续的。5、所有的对象实例都应当在运行时分配在堆上。6、在方法结束后,堆中的对象不会马上被移出,仅仅在垃圾收集的时候才会被移出。7、堆GC是执行垃圾回收的重点区域。
堆内存分为:新生区(新生代)+老年区(老年代)
新生区分为Eden(伊甸园)区和Survivor(幸存者)区。
分区(代)的原因:将对象根据存货概率进行分类,对存货时间长的对象,放到固定区,从而减少扫描垃圾时间及GC频率,针对分类进行不同的垃圾回收算法,对算法进行扬长避短。
对象创建内存分配过程
1、new的新对象先放到伊甸园区,此区大小有限制。
2、当伊甸园的空间中填满时,程序需要创建对象时,JVM的垃圾回收器将对伊甸园区进行垃圾回收(Minor GC)将伊甸园区中的不再被引用的对象进行销毁,再加载新的对象放到伊甸园。
3、然后伊甸园中剩余对象移动到幸存者0区。
4、如果再次触发垃圾回收,此时上次幸存下来存放0区的对象,如果没有回收就会被放到幸存者1区,每次保证会有一个幸存者是空的。
5、如果再次经历垃圾回收,此时会重新放回幸存者0区,接着再去幸存者1区。
6、当参数达到15次数后,对象会去老年代,参数值默认是15,最大值也是15,可以设置改变,-XX:MaxTenuringThreshold=。在对象头中,它是由4位数据来对GC年龄进行保存的,所以最大值为1111,即为15,所以在对象的GC年龄达到15时,就会从新生代转到老年代。
7、在老年区,相对悠闲,当养老区内存不足时,再次触发fullGC,进行养老去的内存清理。
8、若养老区执行了fullGC之后发现依然无法保存对象,就会产生OOM异常。
利用 java visualVM查看垃圾回收过程
字节码解析
什么是STW
STW:stop the world,停止用户线程。JVM调优主要是减少STW。因为停止用户线程会减低用户的体验。
那为什么要停止用户线程?
因为垃圾回收的时候,rootgc进行标记,如果用户线程还在继续,可能原来标记的非垃圾失去引用,成为垃圾,影响垃圾回收。
总结:
JVM虚拟机包括4个部分:类加载器子系统;运行时数据区;字节码执行引擎;本地方法接口。
运行时数据区(Runtime Data Area):存储运行时数据,包含方法区,堆,栈,本地方法栈,程序计数器。