1. 堆与非堆内存
Heap 堆区 | No-Heap 非堆区 |
---|---|
Young Generation 新生代/ Old Generation 旧生代 | Method Area 方法区/ Thread Area 线程区 |
其中新生代又分为: Eden区/Survivor区 | 线程区分为: VM Stack 虚拟机栈/ Runtime Constant Pool常量池/ Program Counter 计数器 |
Heap堆内存 (GC堆)
Java Heap 作为垃圾回收的主要对象,保存了所有Java对象和数组。当没有空间分配,会有OutOfMemoryError错误。
- 被所有线程共享的一块内存区域
- 所有对象实例以及数组都在堆上分配 (all class instances and arrays is allocated.)
- 作为垃圾回收的主要区域
- 没有空间分配,会有OutOfMemoryError错误
Method Area方法区
保存Java类、常量以及静态变量(class文件),作为永久代,也叫做非堆。
- 存储已被虚拟机加载的类信息、常量、静态变量以及编译后的代码
- 内存不足则会抛出OutOfMemoryError: PermGen space
常量池
- 作为方法区的一部分,存储运行时产生的常量,最长使用的是String类的intern方法(属于native方法,作用是如果常量池中存在当前字符串, 就会直接返回当前字符串. 如果常量池中没有此字符串, 会将此字符串放入常量池中后,再返回,这里不做详细说明)
VM Stack虚拟机栈
- 线程私有,与线程的声明周期相同
- 存储局部变量表、操作栈、动态链接、方法出口
- 局部变量表保存了基本数据类型(boolean、byte、char、int等),其中对象引用(reference类型)并不是对象本身而是对象的地址指针或者句柄
其中Heap区域作为垃圾回收的主要操作空间,以及解释下为什么Survivor会有两个?
大的对象直接进入旧生代,长期存活的对象进入旧生代
对于线上环境的内存泄露问题可以采用使用btrace等工具
2. 垃圾回收
Heap堆内存分为新生代、老生代,比例1:2
新生代又分为Eden区和Survivor区,比例8:1:1;Eden区放新生对象、Survivor区放回收后存活的对象。
回收步骤
标记整理清除: 先标记回收对象,然后再清除。会产生碎片。适用于存活对象多、回收对象少。
复制清除: 先复制在清除,但复制的效率较低、占用较大内存。适用于存活对象少、回收对象多。
分代回收: 新生代采用复制清除、老年代使用标记整理清除。这就是为什么分为两代区域保存,其中新生代又Eden空间和Survivor空间,并且比例是8:1:1,
所以,需要有两个Survivor区域用来复制保留的对象。
举例:
Survivor区域有两个是因为需要复制留存的对象,From Survivor和To Survivor也会根据情况来互换。
3.对象创建
首先是在Java方法栈中增加reference数据本地变量表,Java堆中保存实例化的对象,而相关的类型数据则保存在方法区。
伪代码举例:
使用指针访问数据类型的好处是速度较快,但垃圾回收时相比句柄池,对象会被整体移动损耗时间较长