From Survivor
To Survivor
方法区
与Java堆一样,各个线程共享的内存区域,存储已被虚拟机加载的类信息、常量、静态变量、即使编译器编译后的代码等数据。
运行时常量池
方法区的一部分,用于存放编译器生成的各种字面量和符号引用。
运行时常量池相对于class文件常量池的另外一个重要特征是具备动态性,Java语言并不要求常量一定只有编译期才能产生,也就是并非预置入class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中,这种特性被开发人员利用得比较多的便是String类的intern()方法。
直接内存
在JDK1.4中新加入了NIO类,引入了一种基于通道与缓冲区的I/O方法,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。
HotSpot虚拟机对象
对象的创建
在语言层上,创建对象通常仅仅是一个new关键字而已,而当虚拟机遇到一条new执行时,将由一下步骤:
检查类是否加载、解析、初始化过,没有则先执行相应的类加载过程。
在堆中分配内存
划分可用空间:
指针碰撞:堆内存规整
空闲列表:堆内存不规整
并发问题
同步:采用CAS配上失败重试的方式保证更新操作的原子性
把内存分配动作按照线程划分在不同的空间之中进行
将分配到的内存空间都初始化零值
设置对象的类实例、元数据、哈希码、GC分代年龄等信息。
执行方法
对象的内存布局
对象在内存中储存的布局可以分为3块区域:
对象头
对象运行时数据、哈希码、GC分代年龄、锁状态标记、线程持有的锁、偏向线程ID等
类型执行:即对象执向它的类元数据的指针,指明对象数据哪个类的实例。
实例数据
对象真正存储的有效信息
对齐填充
占位符作用
对象的访问定位
句柄定位
直接指针
垃圾收集器
程序计数器、虚拟机栈和本地方法栈这三个区域属于线程私有的,只存在于线程的生命周期内,线程结束之后也会消失,因此不需要对这三个区域进行垃圾回收。垃圾回收主要是针对 Java 堆和方法区进行。
判断对象是否死亡
引用计数算法
给对象添加一个引用计数器,每当有一个地方引用它,计数器值就加1;引用时效时,计算器值就减1;当计数器值为0的对象就是不可能再被使用的。
当两个对象相互引用时,此时引用计数器的值永远不为0,导致无法对它们进行垃圾回收。
public class ReferenceCountingGC {
public Object instance = null;
public static void testGC() {
ReferenceCountingGC objA = new ReferenceCountingGC();
ReferenceCountingGC objB = new ReferenceCountingGC();
objA .instance = objB ;
objB .instance = objA ;
objA = null;
objB = null;
System.gc();
}
}
可达性分析算法
以GC Roots为起始点,从这些节点开始向下搜索,能够搜索到的对象都是存活的,不可达的对象则为不可用。
在Java语言中,可作为GC Roots的对象包括下面几种:
虚拟机栈中引用的对象
方法区中静态属性引用的对象
方法区中常量引用的对象
本地方法栈中Native方法引用的对象
引用类型
无论是引用计数算法还是可达性分析算法判断对象是否存活都与引用有关。在JDK1.2之后,Java对引用的概念进行了扩充,划分为强度不同的四个的引用类型。
强引用
通过new来创建对象的引用类型,被强引用的对象永远不会被垃圾收集器回收。
Object obj = new Object();
软引用
通过SortReference类来实现,只有在内存不足的时候才会被回收。
Object obj = new Object();
SoftReference sr = new SoftReference(obj);
obj = null;
弱引用
通过WeakReference类来实现,只能存活到下一次垃圾收集发生之前。
Object obj = new Object();
WeakReference wr = new WeakReference(obj);
obj = null;
WeakHashMap 的 Entry 继承自 WeakReference,主要用来实现缓存。
private static class Entry<K,V> extends WeakReference implements Map.Entry<K,V>
Tomcat 中的 ConcurrentCache 就使用了 WeakHashMap 来实现缓存功能。ConcurrentCache 采取的是分代缓存,经常使用的对象放入 eden 中,而不常用的对象放入 longterm。eden 使用 ConcurrentHashMap 实现,longterm 使用 WeakHashMap,保证了不常使用的对象容易被回收。
public final class ConcurrentCache<K, V> {
private final int size;
private final Map<K, V> eden;
private final Map<K, V> longterm;
public ConcurrentCache(int size) {
this.size = size;
this.eden = new ConcurrentHashMap<>(size);
this.longterm = new WeakHashMap<>(size);
}
public V get(K k) {
V v = this.eden.get(k);
if (v == null) {
v = this.longterm.get(k);
if (v != null)
this.eden.put(k, v);
}
return v;
}
public void put(K k, V v) {
if (this.eden.size() >= size) {
this.longterm.putAll(this.eden);
this.eden.clear();
}
this.eden.put(k, v);
}
}
虚引用
也称为幽灵引用或者幻影引用,是最弱的一种引用关系。
his.longterm.get(k);
if (v != null)
this.eden.put(k, v);
}
return v;
}
public void put(K k, V v) {
if (this.eden.size() >= size) {
this.longterm.putAll(this.eden);
this.eden.clear();
}
this.eden.put(k, v);
}
}
虚引用
也称为幽灵引用或者幻影引用,是最弱的一种引用关系。