颜群JVM【02】JVM运行时的内存区域
颜群JVM【02】
- JVM运行时的内存区域
- 1 程序计数器
- 2 虚拟机栈
- 3 本地方法栈
- 4 堆
- 堆的划分
- 新生代
- 老生代
- 特点
- 划分新生代老生代的意义
- 异常演示
- 虚拟机参数
- 5 方法区
- 注意
JVM运行时的内存区域
将JVM在运行时的内存,划分为了5个部分,如图所示。
1 程序计数器
程序计数器:行号指示器,指向当前线程所执行的字节码指令的地址(行号)
Test.java -> Test.class
// 假设下述代码是class中的代码:
int num = 1; //1
int num2 = 2 ; //2
if(num1>num2){//3
...//4-10
}else //11
{...
}
while(...)
{}
简单的可以理解为:class文件中的行号
注意:
1.一般情况下,程序计数器 是行号;但如果正在执行的方法是native方法,则程序计数器的值为 undefined。【native方法不在class中】
2.程序计数器是唯一一个 不会 产生 “内存溢出”的区域。
goto的本质就是改变的程序计数器的值(java中没有goto,goto在java中是保留字)
2 虚拟机栈
虚拟机栈的定义:描述 方法
执行的内存模型
方法在执行的同时,会在虚拟机栈中创建一个栈帧:
栈帧中包含:方法的局部变量表,操作数栈、动态链接、方法出口信息等:
动态链接:符号引用会在实际执行时转为不同的地址;转换的行为会在每一次运行时都发生。
当方法太多时,就可能发生栈溢出异常StackOverflowError
public static void main(String[] args) {main(new String[]{"abc","abc"});
}
3 本地方法栈
原理和结构与虚拟机栈一致
不同点: 虚拟机栈中存放的 jdk 或我们自己编写的方法,而本地方法栈调用的是操作系统底层的方法。
4 堆
-
堆存放对象实例(数组【数组也是对象】、对象)
-
堆是jvm区域中最大的一块空间,在jvm启动时该空间就已经创建完毕
-
堆是GC主要管理的区域
-
堆本身是线程共享,但在堆内部可以划分出若干个线程私有的空间(该空间严格来说叫缓冲区)
-
堆允许物理空间不连续,只要逻辑连续即可
堆的划分
堆可以分为新生代、老生代 。大小比例,新生代:老生代= 1:2 ;
新生代中包含eden、s0、s1。大小比例,eden:s0:s1 = 8:1:1 【eden: 伊甸园;s: survive,幸存者】
新生代
-
新生代的使用频率一般在90%【eden 80 , s0/s1 10】。
-
在使用时,同一时刻只能使用一个eden和一块s区间(s0或s1)。也就是说:新生代在使用时,只能同时使用一个s区:底层采用的是复制算法,为了避免碎片产生(将不连续的对象复制到连续空间中)
-
新生代存放:
-
1.生命周期比较短的对象 ;
-
2.小的对象;
-
反之,存放在老生代中;
对象的大小,可以通过参数设置 -XX:PretenureSizeThredshold 。小于设置值的就是小对象。
一般而言,大对象一般是集合、数组、字符串。
-
-
MinorGC回收新生代中的对象。
- 如果Eden区中的对象在一次回收后仍然存活,就会被转移到 s区中;
- 之后,如果MinorGC再次回收,已经在s区中的对象仍然存活,则年龄+1。
- 如果年龄增长一定的数字【当前版本jdk默认值是15,大于15也就是16的话,对象就会被转移】,则对象会被转移到 老生代中。
- 简言之:在新生代中的对象,每经过一次MinorGC,有三种可能:
- 1.从eden —> s区
- 2.(已经在s区中)年龄+1
- 3.转移到老生代中
老生代
-
老生代存放:
- 生命周期比较长的对象;
- 大的对象;
-
老生代使用的回收器:MajorGC\FullGC
特点
-
新生代特点:
- 大部分对象都存在于新生代
- 新生代的回收频率高、效率高
-
老生代特点:
- 空间大
- 增长速度慢
- 回收频率低
划分新生代老生代的意义
可以根据项目中对象大小的数量,设置新生代或老生代的空间容量,从提高GC的性能。
异常演示
如果对象太多,也可能导致内存异常。
堆内存溢出的示例:java.lang.OutOfMemoryError: Java heap space
import java.util.ArrayList;
import java.util.List;public class TestHeap {public static void main(String[] args) {List list = new ArrayList() ;while(true){list.add( new int[1024*1024]) ;}}
}
虚拟机参数
-Xms128m :JVM启动时的大小
-Xmn32m:新生代大小
-Xmx128m:总大小
JVM堆的总大小= 新生代 + 老生代 + 永久代【永久代已经被废弃,之前是用来存放元数据】
所以:
JVM堆的总大小= 新生代 + 老生代
5 方法区
存放:类的元数据(类的描述信息)、常量池、方法信息(方法数据、方法代码)
gc:类的元数据(描述类的信息)、常量池
常量池:存放编译期间产生的字面量(“abc”)、符号引用
图:方法区与其他区域的调用关系
方法区中数据如果太多,也会抛异常OutOfMemory异常
注意
导致内存溢出的异常OutOfMemoryError,除了虚拟机中的4个区域以外,还可能是直接内存。
在NIO技术中会使用到直接内存。