java内存总结

一、java内存分区

一共分为6个区域:

1、方法区(也叫非堆区)和堆区,另外还有直接内存即堆外内存,这三个区域都是线程共享的内存区域。

2、虚拟机栈,本地方法栈,程序计数器。

这6个区域,出了程序计数器区域不可能发生内存问题,其他5个区域都可能发生内存问题。

这里指的内存问题包括内存泄漏和内存溢出。

内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;

内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。

二、各区内存报错分类

1、虚拟机栈

虚拟机栈区域存放的是函数参数,动态链接,方法出口,局部变量表等等栈帧信息。

这个区域通常发生内存溢出,比如死循环,递归深度过深就会报错:StackOverflowError

另外极少情况下,栈内存不够用,才会出现OutOfMemeroyError

2、本地方法栈

这个区域和虚拟机栈非常类似,唯一区域是本地方法栈是为跨语言调用设计的。

一般的java程序员用得很少,一般在跨语言调用时出现,当java调用C的动态链接库里面的方法时,就会出现和虚拟机栈内存溢出一样的两种报错

3、Java堆

这个区域报错很常见,大量的java对象就存在这里。

这个区域中可以支持划分出部分给每个线程独享,叫Thread Local Allocation Buffer即TLAB。

内存溢出时,只会抛出OutOfMemoryErro异常

4、方法区

这个区域存放java类里面静态变量,常量,类加载信息,即时编译信息等。是线程共享的内存区域。也叫非堆(Non-Heap)

这个区域是用永久代的方式进行内存管理。也就是说只有触发Full GC才会回收。通常回收条件苛刻,一旦有问题,很容易内存报错。

内存溢出是,会抛出OutOfMemoryErro异常

另外运行时常量池是方法区的一部分。比如:

String a = "sdfdfsd" ;

String b = "sdfdfsd" ;

直接赋值字符串是在堆里申请空间,把引用存入常量池 ,

当再次赋值时,JVM会判断常量池中是否已经存在,如果存入,不会再到堆申请内存,而是直接返回其引用。所以 a == b 是true

String c = new String("sdfsdf");

String d = new String("sdfsdf");

这个就是完全在堆里。由于c和d是不同的指针,指向的内存区域不一致,所以c == d 为false

5、直接内存(堆外内存)

这个区域也是容易发生内存溢出。通常是在使用JDK的NIO的方法时用到。

HeapByteBuffer heapByteBuffer     = ByteBuffer.allocate(1024);
DirectByteBuffer directByteBuffer = ByteBuffer.allocateDirect(1024);

这段代码只是为了说明区别,其实HeapByteBuffer 和 DirectByteBuffer是ByteBuffer的内部类,不能被外部引用。上面的代码是不能用的。

allocateDirect就是用的是堆外内存,即当前java进程外部的操作系统剩余内存

allocate是申请java当前进程的正常堆区的内存

控制堆外内存的参数是: -XX: MaxDirectMemorySize,不指定时,和-Xmx即堆空间一样大小。

堆外内存溢出有一个明显特征:

在HeapDump文件中不会看见明显的异常,而且Dump文件很小。并且程序中直接或者间接使用了堆外内存。

比如spark1.6以后版本,在shuffer阶段各节点使用netty进行传输磁盘文件数据,很容易堆外内存溢出。

这次写到这里。

 

原文地址:https://www.cnblogs.com/geektcp/p/9930890.html