一、java内存模型堆
1、堆特点
堆是用于存放对象的内存区域。因此,它是垃圾收集器(GC)管理的主要目标。
- 堆在逻辑上划分为“新生代”和“老年代”,在细致一点可分为Eden空间、From Survivor空间、To Survivor。由于JAVA中的对象大部分是朝生夕灭,还有一小部分能够长期的驻留在内存中,为了对这两种对象进行最有效的回收,将堆划分为新生代和老年代,并且执行不同的回收策略。不同的垃圾收集器对这2个逻辑区域的回收机制不尽相同。这块以后会细讲。
- 内存的分配角度看,线程共享的java堆可能划分出多个线程私有的分配缓冲区。
- 堆一般实现成可扩展内存大小,使用“-Xms”与“-Xmx”控制堆的最小与最大内存,扩展动作交由虚拟机执行。但由于该行为比较消耗性能,因此一般将堆的最大最小内存设为相等。
- 堆占用的内存并不要求物理连续,只需要逻辑连续即可。
- 堆的生命周期是随着虚拟机的启动而创建。
- 堆是所有线程共享的内存区域,因此每个线程都可以拿到堆上的同一个对象。
2、堆异常
当堆无法分配对象内存且无法再扩展时,会抛出OutOfMemoryError异常。
package com.sl.common;
import java.util.ArrayList;
import java.util.List;
/**
* @author shuliangzhao
* @Title: Test
* @ProjectName spring-boot-learn
* @Description: TODO
* @date 2019/9/24 19:41
*/
public class Test {
public static void main(String[] args) {
List<Object> list = new ArrayList<>();
while (true) {
list.add(new Test());
}
}
}
上述代码中对象不断的被创建而不进行引用释放,导致GC无法回收堆内存,最终OutOfMemoryError。
3、堆的性能调优参数
- -Xms and -Xmx (or: -XX:InitialHeapSize and -XX:MaxHeapSize):指定JVM的初始和最大堆内存大小,两值可以设置相同,以避免每次垃圾回收完成后JVM重新分配内存。
- Xmn:设置年轻代大小。整个堆大小=年轻代大小 + 年老代大小 + 持久代大小。所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
- -Xss:设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。
- -XX:+HeapDumpOnOutOfMemoryError and -XX:HeapDumpPath:让JVM在发生内存溢出时自动的生成堆内存快照(堆内存快照文件有可能很庞大,推荐将堆内存快照生成路径指定到一个拥有足够磁盘空间的地方。)
- -XX:PermSize and -XX:MaxPermSize:设置永久代大小的初始值和最大值(默认:最小值为物理内存的1/64,最大值为物理内存的1/16,永久代在堆内存中是一块独立的区域,这里设置的永久代大小并不会被包括在使用参数-XX:MaxHeapSize 设置的堆内存大小中)
- -XX:PretenureSizeThreshold :令大于这个设置值的对象直接在老年代分配。这样做的目的是避免在Eden区及两个Survivor区之间发生大量的内存复制
二、本地方方法
本地方法栈的功能和特点类似于虚拟机栈,均具有线程隔离的特点以及都能抛出StackOverflowError和OutOfMemoryError异常。不同的是,本地方法栈服务的对象是JVM执行的native方法,而虚拟机栈服务的是JVM执行的java方法。如何去服务native方法?native方法使用什么语言实现?怎么组织像栈帧这种为了服务方法的数据结构?虚拟机规范并未给出强制规定,因此不同的虚拟机实可以进行自由实现,我们常用的HotSpot虚拟机选择合并了虚拟机栈和本地方法栈。