运行时数据区域,还有两组成部分:堆和方法区,和栈、程序计数器不同,它们是线程共享的
如图:栈里的局部变量中存了堆上对象的引用:
ArrayList<Object> list = new ArrayList<>();
while (true) {
list.add(new byte[1024 * 1024]);
}
一直new对象往堆里放,最终达到堆内存上限值后堆内存溢出OutOfMemory:Java heap space
调整下上面的代码:
ArrayList<Object> list = new ArrayList<>();
while (true) {
System.in.read();
System.out.println("往堆中放入一次...");
list.add(new byte[1024 * 1024 * 10]);
}
使用阿尔萨斯工具来看JVM信息:
dashboard -i 刷新频率(毫秒)
或者直接使用memory只查看内存:
memory
随着堆中对象变多,used即将达到total时,total值变大,但最大只能到与max相等:
但并不是当used = max = total的时候,堆内存就溢出了!! 这和垃圾回收有关。
添加虚拟机参数:
-Xmx值 -Xms值
//eg:
-Xmx1g -Xms1g
在Dockerfile中可写:
ENV JAVA_OPTS="-Xms512m -Xmx512m "
其中:
开发中,把-Xmx和-Xms设为相同的值,如此,程序启动后的可用内存就是最大内存,无需向JVM频繁申请,以减少申请内存的时间开销。
直接内存不属于Java运行时的内存区域,用途:
Java堆中的对象如果不再使用要回收,回收时会影响对象的创建和使用
普通IO,是文件先读到内存(缓存区),再复制到JVM堆中。引入直接内存则不用复制这一步,直接让堆中存一个引用
JDK8及以后,存方法区的数据
使用ByteBuffer创建直接内存上的数据:
public class Demo2 {
public static int size = 1024 * 1024 * 100;
public static List<ByteBuffer> list = new ArrayList<>();
public static int count = 0;
public static void main(String[] args) throws Exception {
System.in.read();
while(true){
ByteBuffer directSpace = ByteBuffer.allocateDirect(size);
list.add(directSpace);
System.out.println(++count);
Thread.sleep(5000);
}
}
}
阿尔萨斯查看:
修改直接内存的上限:
-XX:MaxDirectMemorySize=大小
//eg:-XX:MaxDirectMemorySize=300m
出现直接内存溢出: