java与C++之间有一堵内存动态分配和垃圾收集技术所围成的高墙,墙外的人想进去,墙里的人却想出来。 --摘自《深入理解Java虚拟机》
C++认为内存很重要,所以一定要给人管理,java同样认为内存很重要,所以一定不能给人管理。 --忘了哪看来的
计算机的世界中,所有问题都可以通过引入一个中间角色来解决(jvm就是典型)。
a.为什么做内存划分?
java进程下的占用的一块内存,划分成5个逻辑空间,对每一块做特定的处理数据方式和内存管理方式,会大大提升内存效率。
b.划分情况
上边提到的5个内存空间分别为
栈、堆、方法区、程序计数器、本地方法栈 (注:此逻辑为划分规范,即各个JVM虚拟机(eg : HotSpot和此规范可理解为接口和实现类的关系)实现需参考此规范)
堆:所有几乎所有new出来的东西,GC的主要作案地点非常血腥,然而真实情况是随着新技术方案的出现未必所有new的对象都会在堆申请内存,后面展开讨论。
-
Exception in thread “main”: java.lang.OutOfMemoryError: Java heap space
(对象不能分配在堆中,可能程序出现死循环)
栈:存放的是一个个方法对应的栈桢,而各个栈桢存的是局部变量表、操作数栈、动态链接、方法出口,方法调用链结束对应栈桢压栈和弹栈,线程结束当前栈销毁。
此区域会出现两种OOM情况StackOverflowError:线程请求的栈深度大于虚拟机所允许的深度;OutOfMemoryError:当扩展时无法申请到足够的内存(当前大部分的虚拟机 都会动态扩展,然而单线程情况下只能实验出StackOverflowError异常)
Exception in thread "main" java.lang.StackOverflowError
方法区:虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等
JDK8后此区域的hotspot实现由永久带换为元空间
-
Exception in thread “main”: java.lang.OutOfMemoryError: PermGen space
(类或者方法不能被加载到老年代。它可能出现在一个程序加载很多类的时候,比如引用了很多第三方的库、或者使用了类似CGlib技术动态生成大量字节码文件)
程序计数器:指向下一条指令的地址,每个线程有一个(唯一无OOM情况的区域)
本地方法栈:native方法,本地实现,当调用native方法时,程序计数器值为undefined
Exception in thread “main”: java.lang.OutOfMemoryError: unable to create new native thread
(没有空间给新的线程使用,可看情况适当调小栈大小或堆大小等方法换取更多有效线程并发)
还有几种OOM异常不再逐个列举,重点只需对异常产生区域和原因的理解,来快速排查及做出相应策略,然而最好的状态是不要给机会让OOM出现,做好合理架构和监控。
还有一块不在JVM规范中的堆外内存,也称作直接内存。
NIO中一般使用直接内存与外界通讯并作为缓存使用,目的是避免在java堆和native堆来回复制数据,速度快。