上图中,展示了 不同 JDK 版本中的 JVM 对不同操作系统在底层硬件和指令上的处理
;同时,通过 javac 指令产生的 .class 文件对于不同的 JVM 都是可以运行的
,故而同一套的 java 程序可以不同操作系统上运行
如果想要学习 JVM 底层运行步骤,可以利用 idea 的Open in Terminal 或者 命令行界面
,进入到对应的 .class 文件路径,输入 javap -c 类名.class > 文件名.txt
;将程序的字节指令码保存到指定的文件中,至于路径,就是 .class 文件所在路径
注意: 字节指令码各自含义,可参考 JVM指令手册
主要有:类装载子系统、运行时数据区(内存模型)、字节码执行引擎
字节码执行引擎作用:
(1)修改程序计数器中的数值
(2)java 程序利用 javac 命令转换成为.class文件(指令文件)之后,由字节码执行引擎执行
存放 new 出来的对象
堆的划分:其中默认,Old区占堆的2/3
;Eden区和Survivor区共占1/3
;Eden区占1/3的80%
,而Survivor区占1/3的20%
;Survivor区的两个部分,是平分
的;Eden区与Survivor区合称为年轻代
(1)Eden区:伊甸园区(亚当夏娃相遇的地方,也就是新生的开始),又称青年代
(2)Survivor区:幸存区,又称中年代;平均分为两部分,Survivor0区和Survivor1区
(3)Old区:永久区,又称老年代
对象在堆中的具体流转,在之后与垃圾回收、简单优化一起;可见 第三部分:垃圾回收与简单优化(包含堆内存具体流程)
又称为 线程栈
,存放 局部变量
栈:遵循 FILO(先进后出)
原则
上述程序中,在main线程中,main方法调用了compute方法
注意: 一个方法对应一个栈帧内存区域
;每个方法各自的局部变量存放在自己的栈帧内存区域
栈又分为局部变量表、操作数栈、动态链接、方法出口等
(1)局部变量表:顾名思义,存放局部变量的地方
(2)操作数栈:临时存放操作数的区域,之后会将操作数与局部变量关联,放入局部变量表;操作数,有点类似于数据值(如上述程序中的1、2、10)
(3)动态链接:
(4)方法出口:记录调用该方法的地方及行数
记录线程运行到的位置,主要是在多线程运行切换时使用到
每运行一行代码时,都要修改一次,而这个修改,就是由 字节码执行引擎
来修改的
java1.8之前称为永久态,java1.8及以后又称为元空间
存放常量、静态变量、类信息、方法信息;其实就是不会改变
的一些数据信息
java语言是1995年
面世的,之前程序都是使用C语言等实现的;java面世之后,java程序调用C程序就是本地方法调用的
当然,在后来,随着技术的发展,出现了很多跨语言调用的技术,也就不再使用本地方法调用了
本地方法用关键字 native
修饰
而每个本地方法在运行过程中,也是需要使用到内存;就是本地方法栈
堆、栈、方法区、本地方法栈关系图:
当Eden区内存满了的时候,触发第一次的minor gc(小范围垃圾回收,即检测年轻代范围)
;将非垃圾对象移动到Survivor0中(标记加1),而垃圾对象直接被清除。
当Eden区第二次满了的时候,也做同样操作;不过此时会将Survivor0一起扫描,然后将非垃圾对象移动到Survivor1中(标记加1),而垃圾对象直接被清除。
第三次的时候,再移动至Survivor0中;如此循环。当标记达到15时,就会被直接放入到Old区当中
使用命令行
界面,输入 jvisualvm
指令,调出工具;在工具中,安装visual gc插件;安装好后,即可使用
当Old区内存满了的时候,就会触发 full gc(大范围垃圾回收,即检测整个堆范围)
;
而不管是 minor gc 还是 full gc
都是由字节码执行引擎来执行的
从上一部分讲述的堆、栈、方法区、本地方法栈关联出发;其实,gc root 就是栈中的局部对象变量、方法区中静态对象变量、本地方法栈中的对象变量
利用 gc root 来开始,往堆内存中查找 new对象,new对象 中可能又存在对象,故而就这么一直查找下去,直到一个 new对象 中完全没有对象;这样就会形成一个对象之间的关系链
在程序进行 gc 操作的时候,会将整个程序都进行停止;称为 Stop-The-World
在 minor gc 时,STW时间极短,都可以忽略不计
的;而 full gc 时,由于是整个堆范围进行 gc ,故而会STW较久
,故而会出现性能问题
,故而需要尽可能的减少 full gc 的次数
当一个方法运行结束后,栈中的局部对象变量、方法区中静态对象变量、本地方法栈中的对象变量
就会被清除;也就是 gc root被清除;这就会造成 gc关系链无根,那么关系链中余下的所有对象就都是垃圾对象
(1)对象标记达到了15:
(2)对象动态年龄判断:当一个对象的内存大小达到或超过
Survivor0(或者是Survivor1)内存的50%
时,会直接被放入Old区
线程运行每秒产生60MB
对象,13秒
达到Eden区内存极限,进行minor gc
;前12秒的 12 * 60MB
会被直接清除,但最后一秒的60MB会放入Survivor0
;这时,60MB超过了Survivor0的50%
,就会被直接放入Old区
;那5、6分钟之后,Old区就会满内存,进行 full gc
每5、6分钟进行一次 full gc,太过于频繁;STW严重
;故而要优化
优化方案:
在原来的JVM参数配置中,加入了 -Xmn2048M
;该参数意思就是设置年轻代(即Eden区加Survivor区)内存大小;此时,Eden区就是1600MB,Survivor0和Survivor1各是200MB;这时Eden区就是26秒左右才会满,而60MB又小于Survivor0的50%
;故而不会被放入Old区,也就降低了 full gc 的次数,减少STW的次数