-
运行时数据区
-
方法区(Method Area)
- 运行时常量池
-
元空间(Meta Space)JDK 8 +
- 字符串常量池
- 静态变量
- 类型信息
-
溢出
- -XX: MaxMetaspaceSize
- -XX: MetaspaceSize
- -XX: MinMetaspaceFreeRatio
- 虚拟机栈(VM Stack)
- 本地方法栈(Native Method Stack)
-
堆(Heap)
- 溢出(Java.lang.OutOfMemoryError、Java heap space)
- 可以通过Java内存(映像)分析工具(hprof文件分析工具)打开内存堆转储快照分析
- 程序计数器(Program Counter Register)
-
HotSpot不区分虚拟机栈和本地方法栈,同时也不支持栈的动态扩展,栈容量由`-Xss`参数设定,HotSpot的栈溢出抛出的异常全都是StackOverFlowError。
-
溢出
- 使用-Xss参数减少栈内存容量。
- 定义了大量的本地变量,增大此`方法帧`中`本地变量表`的长度。
- 通过不断建立线程的方式,在HotSpot上也是可以产生内存溢出异常的。
-
直接内存(Direct Memory)
-
溢出
- 使用unsafe直接分配本机内存,模拟内存溢出
- 由直接内存导致的内存溢出,一个明显的特征是在Heap Dump文件中不会看见有什么明显的异常情况,如果读者发现内存溢出之后产生的Dump文件很小,而程序中又直接或间接使用了DirectMemory(典型的间接使用就是NIO),那就可以考虑重点检查一下直接内存方面的原因了。
-
垃圾收集器
- 程序计数器
- 虚拟机栈
- 本地方法栈
-
Java堆
-
对象已死?
- 引用计数法
- 可达性分析算法(Reachability Analysis)
- 生存与死亡?(两次标记过程)
-
方法区
- 回收方法区的废弃常量和不再使用的类型(方法区垃圾收集的“性价比"通常也是比较低的)
-
垃圾收集算法
-
对象消亡
- “引用计数式垃圾收集”(Reference Counting GC)(直接垃圾收集)
- “追踪式垃圾收集”(Tracing GC)(间接垃圾收集)
-
分代收集理论
- 弱分代假说(Weak Generational Hypothesis):绝大多数对象都是朝生夕灭的
- 强分代假说(Strong Generational Hypothesis):熬过越多次垃圾收集过程的对象就越难以消亡
-
收集类型
-
部分收集(Partial GC)
- 新生代收集(Minor GC/Young GC)
- 老年代收集(Major GC/Old GC)
- 混合收集(Mixed GC)
- 整堆收集(Full GC)
-
不同区域安排不同的算法
- 标记-复制算法
- 标记-清除算法
- 标记-整理算法
-
Java堆
- 新生代(Young Generation)
- 老年代(Old Generation)
- 跨代引用假说(Intergenerational Reference Hypothesis)
- 这3个区域随线程而生,随线程而灭,栈中的栈帧随着方法的进入和退出而有条不紊地执行着出栈和入栈操作。每一个栈帧中分配多少内存基本上是在类结构确定下来时就已知的(尽管在运行期会由即时编译器进行一些优化,但在基于概念模型的讨论里,大体上可以认为是编译期可知的),因此这几个区域的内存分配和回收都具备确定性,在这几个区域内就不需要过多考虑如何回收的问题,当方法结束或者线程结束时,内存自然就跟随着回收了。
- 两个区域则有着很显著的不确定性,一个接口的多个实现类需要的内存可能会不一样,一个方法所执行的不同条件分支所需要的内存也可能不一样,只有处于运行期间,我们才能知道程序究竟会创建哪些对象,创建多少个对象,这部分内存的分配和回收是动态的。垃圾收集器所关注的正是这部分内存该如何管理。