一个方法对应一块栈帧内存区域,遵循栈的数据结构FILO(即first in last out,先进后出)原则;
栈帧内部可以放局部变量表、操作数栈【就是在程序运行过程中操作数的临时中转内存空间】、动态链接【在程序运行过程中把符号引用转换为直接引用】、方法出口;
程序计数器
每一个线程都有一个程序计数器,用来记录即将执行的代码行号;
每执行完一行代码,字节码执行引擎都会去修改程序计数器的值;
堆
new的对象一般放在堆的Eden区;
字节码执行引擎会对Eden区进行minor gc,如果在Eden的minor gc中存活下来,会被挪到s0区,分代年龄会+1,再次minor gc还存活就会挪到s1区,分代年龄会+1,再次minor gc还存活就会挪到s0区,分代年龄+1,就这样s0与s1来回倒腾,直到分代年龄到15,就会被挪到老年代区;
如果老年代内存满了会触发full gc【也可以称为major gc】;
如果老年代gc不了,就会OOM了;
在gc的过程中可能会触发STW机制【Stop The World】,即停止用户线程,也就是说在GC时,会停止所有的用户线程,GC时间过长就会影响用户体验,所以说JVM调优主要目的也就是减少GC,无论是minor gc还是full gc,但主要是减少full gc,因为ful gc收集堆的时间比较长,STW时间也会更长;
为什么要在GC时设置STW机制呢?因为GC需要找非垃圾对象,如果不停止用户线程,有可能已经找到的非垃圾对象由于线程持续执行变成了垃圾对象;
可以自己写一个死循环,在循环内创建被外部引用的对象,然后在cmd使用jvisualvm命令打开JDK自带的JVM调优工具观察上述过程。
方法区
运行时常量池、静态变量、类信息就放在方法区;
本地方法栈
native的方法都是本地方法,底层由C语言实现;
本地方法内存就分配在本地方法栈中;
JVM内存参数设置
Spring Boot程序的JVM参数设置格式【Tomcat启动直接加在bin目录下catalina.sh文件里】:
java -Xms2048M -Xmx2048M -Xmn1024M -Xss512K -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M -jar microservice-eureka-server.jar
关于元空间的JVM参数有两个:-XX:MetaspaceSize=N和-XX:MaxMetaspaceSize=N,对于64位JVM来说,元空间的默认初始大小是21MB,默认的元空间最大值是无限。
-XX:MaxMetaspaceSize:设置元空间最大值,默认是-1,即不限制,或者说只受限于本地内存大小。
-XX:MetaspaceSize:指定元空间的初始空间大小,以字节为单位,默认是21M,达到该值就会触发full gc;同时收集器会对该值进行调整,如果释放了大量的空间,就适当降低该值,如果释放了很少的空间,那么在不超过-XX:MaxMetaspaceSize【如果设置了】的情况下,适当提高该值。
由于调整元空间的大小需要full gc,这是非常耗性能的操作,如果应用在启动时发生大量full gc,通常都是由于永久代【JDK8之前方法区叫永久代】或元空间【JDK8之后方法区叫元空间】发生了大小调整,这种情况,建议在JVM参数中把MetaspaceSize和MaxMetaspaceSize设置成一样的值,并设置得比初始值要大,对于8G物理内存的机器来说,一般可以设置为256M。
-Xss设置值越小,说明一个线程栈里能分配的栈帧就越少,但是对于JVM整体来说能开启的线程数就会更多,反之亦然。
其他参数
-XX:SurvivorRatio=8
-XX:MaxTenuringThreshold=5 【设置分代年龄】
-XX:PretenureSizeThreshold=1M【设置大对象,对于大多数系统,1M已经很大了】