java虚拟机运行时数据区
Java内存模型的三大特性
特性 | 描述 |
---|---|
原子性 | 一个或多个操作要么全部执行,要么都不执行,不可被中断 |
可见性 | 当一个线程修改了共享变量的值,其他线程能够立即得知这个修改 |
有序性 | 程序执行的顺序按照代码的先后顺序执行,但多线程环境下可能会重排序 |
Java内存模型和JVM内存结构
组件 | 描述 | 线程共享/私有 | 备注 |
---|---|---|---|
程序计数器 | 指示下一条要执行的字节码指令的地址 | 私有 | 线程私有,每个线程独立拥有 |
Java虚拟机栈 | 描述Java方法执行的内存模型,包含局部变量表、操作数栈等 | 私有 | 线程私有,生命周期与线程相同 |
本地方法栈 | 为虚拟机使用到的Native方法服务 | 私有 | 线程私有,类似于Java虚拟机栈,但用于Native方法 |
Java堆 | 垃圾收集器管理的主要区域,存储对象实例和数组 | 共享 | 线程共享,新生代和老年代等区域 |
方法区/元空间 | 存储类信息、常量、静态变量等 | 共享 | JDK 1.8及以后用元空间代替方法区,使用本地内存 |
方法区(Method area):
属于共享内存区域,存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。堆区(Heap):
线程共享,主要是存放对象实例和数组。
内部会划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer, TLAB)。 可以位于物理上不连续的空间,但是逻辑上要连续。
OutOfMemoryError
:如果堆中没有内存完成实例分配,并且堆也无法再扩展时,抛出该异常。虚拟机栈(VM Stack):
线程私有,生命周期和线程一致。
描述的是 Java 方法执行的内存模型 每个方法在执行时都会床创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
每一个方法从调用直至执行结束,就对应着一个栈帧从虚拟机栈中入栈到出栈的过程。
StackOverflowError
:线程请求的栈深度大于虚拟机所允许的深度。
OutOfMemoryError
:如果虚拟机栈可以动态扩展,而扩展时无法申请到足够的内存。本地方法栈(Native Method Stack):
区别于 Java 虚拟机栈的是,Java 虚拟机栈为虚拟机执行 Java 方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。
也会有 StackOverflowError 和 OutOfMemoryError 异常。程序计数器(Program Counter Register):
内存空间小,线程私有。
字节码解释器工作是就是通过改变这个计数器的值来选取下一条需要执行指令的字节码。
指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖计数器完成。
堆区内存划分
堆区 = 新生代 + 老年代
新生代= Eden区 + Survivor区(from)+ Survivor区(to)
老年代= Tentured区
默认的
新生代:老生代= 1:2
Eden:from:to = 8:1:1
所谓的新生代和老年代是针对于分代收集算法来定义的。
JVM 每次只会使用 Eden 和其中的一块 Survivor 区域来为对象服务,所以无论什么时候,总是有一块 Survivor 区域是空闲着的。
因此,新生代实际可用的内存空间为 9/10 ( 即90% )的新生代空间;
GC 分为两种:老生代中采用标记-清除算法的Full GC ( 或称为 Major GC )和新生代中采用复制算法的Minor GC。新生代是 GC 收集垃圾的频繁区域;
如果对象经过一次Minor GC还存活,并且又能被Survivor空间接受,那么将被移动到Survivor空 间当中。并将其年龄设为1,对象在Survivor每熬过一
次Minor GC,年龄就加1,当年龄达到一定的程度(默认为15)时,就会被晋升到老年代 中了,当然晋升老年代的年龄是可以设置的。
其实新生代和老年代就是针对于对象做分区存储,更便于回收
JVM的堆区内存配置与调优参数
参数 | 描述 | 示例 | 等价参数(如有) |
---|---|---|---|
-Xms | 设置堆的初始内存大小。 | -Xms256m | -XX:InitialHeapSize |
-Xmx | 设置堆的最大内存大小。 | -Xmx512m | -XX:MaxHeapSize |
-Xmn | 设置新生代的大小(同时设置最小值和最大值)。 | -Xmn256m | 无直接等价,但影响新生代 |
-XX:NewSize | 设置新生代的初始大小(JDK 8之前常用)。 | -XX:NewSize=128m | -Xmn (间接) |
-XX:MaxNewSize | 设置新生代的最大大小(JDK 8之前常用)。 | -XX:MaxNewSize=256m | -Xmn (间接) |
-XX:NewRatio | 用于设置老年代与新生代的比例,例如-XX:NewRatio=2保持默认比例 | -XX:NewRatio=2 | 无直接等价 |
-XX:SurvivorRatio | 设置Eden区与Survivor区的比例。 | -XX:SurvivorRatio=8 | 无直接等价 |
-XX:MetaspaceSize | 设置元空间的初始大小(JDK 8及以后)。 | -XX:MetaspaceSize=64m | 无直接等价(针对永久代替代) |
-XX:MaxMetaspaceSize | 设置元空间的最大大小(JDK 8及以后)。 | -XX:MaxMetaspaceSize=512m | 无直接等价(针对永久代替代) |
注意事项
-
在JDK 8及以后的版本中,
-XX:PermSize
和-XX:MaxPermSize
参数已经被移除,因为永久代(PermGen space)被元空间(Metaspace)所取代。 -
-Xmn
参数在JDK 8及以后版本中仍然可用,它同时设置了新生代的初始大小和最大大小,简化了配置过程。 -
-XX:NewRatio
参数用于设置新生代与老年代的比例,而-XX:SurvivorRatio
则用于调整新生代内部Eden区与Survivor区的比例。 -
堆区内存的配置应根据应用程序的实际需求进行调整,以避免内存溢出(OutOfMemoryError)或不必要的内存浪费。
-
使用JVM监控工具可以帮助分析堆内存的使用情况和垃圾回收的性能,从而进行更精确的调优。
-
配置自定义jvm属性
-Dvar1=123456 \
-Dvar4encoding=GBK"
-D固定写法,后面加变量名称
var1变量名称,程序可以通过System.getProperty(“var”)获取该值
如果有多行在值的后面加空格,加斜杆,表示换行
如果是最后一行在值的后面直接加双引号,不要加空格