说在前面的话
<深入理解java虚拟机> link 是一本jvm入门经典,推荐所有java工程师阅读,并应该多读,不同阶段读. 这篇博客就是为了总结本人从该书中的领悟.
运行时的数据分区

- 程序计数器:线程执行的字节码行号.
- 虚拟机栈:生命周期和线程相同,描述的是java方法执行的内存模型, 每个方法执行的同时都会创建一个栈帧(stack frame).存放局部变量、操作数栈、动态链接、方法出口等信息.
- 堆:存放对象实例.随着JIT编译器、逃逸分析技术,栈上分配、标量替换导致不一定所有的对象都在堆上.
- 方法区:虚拟机加载的类信息、常量、静态变量、JIT编译的代码。堆的一个逻辑部分。hotspot使用永久代实现方法区,会受限于MaxPermSize
- Direct Memory: DirectByteBuffer分配堆外内存.不受-Xmx参数控制.
java对象
java 对象的组成: 对象头(header)+实例数据(instance data)+对齐填充
- 对象头:类的元数据、hash码、gc分代).
- 实例数据:包括父类和自身定义的字段内容.
- 对齐填充,保证8字节的整数倍.
对象的访问定位,通过栈上的reference操作堆.
内存/栈溢出
-
heap oom,-Xmx -Xms
Memory Leak:内存无法被回收,GC Roots引用链. Memory overflow:调大内存,优化代码. -
stack overflow(虚拟机栈和本地方法栈)
栈深度支持到1000-2000,如果超过会导致stackoverflow. 如果线程数量过多,则会报错out of memory(受限于一个进程可用内存-堆内存-方法区内存),每个线程需要分配一个栈容量,参数为-Xss. -
方法区和运行时常量池的溢出
String.intern :常量池. 方法区,除了已加载的类,还有许多动态生成类,如cglib、动态代理、osgi、jsp等。 -
本机直接内存directMemory
用反射操作unsafe.
GC
对象存活的判定算法:判断对象和GCROOT引用链是否可达来判断对象是否已死. GC root包括:
- 虚拟机栈的本地变量表引用的对象.
- 方法区中的静态变量引用的对象.
- 方法区中常量池引用的对象.
- 本地方法栈JNI引用的对象
引用的分类, 由强到弱:
| 标题1 | 标题2 | 标题3 |
|---|---|---|
| 强引用 | Object o = new Object | 永远不会回收被引用的对象 |
| 软引用 | SoftReference | 在内存溢出前被回收 |
| 弱引用 | WeakReference | 活到下一次gc. |
| 虚引用 | PhantomReference | 无法通过引用获取,在被回收时收到通知 |
方法区的GC,回收效率低,效果差:
- 无效的常量,如"abc"进入了方法区,但没有地方引用.
- 无效的类信息, 如所有实例被回收/classLoad卸载.
- 反射生成的类, 需要虚拟机具备类卸载功能.
垃圾收集算法:
- 标记清除算法. 产生空间碎片,标记和清除的效率都比较低.
- 标记复制算法. 内存可用空间降低, 新生代使用,由于大部分对象朝生夕死, 不需要1:1,一般是8:1:1.
- 标记整理算法. 老生代使用.