
为什么使用元空间替代永久代呢?
如下图为栈帧操作
负责执行虚拟加载进来的字节码
负责将本地Class文件加载进入JVM。
类加载器负责将Java程序运行过程中将Class文件动态加载至JVM中,
Java中类加载器有三种:启动类加载器(BootstrapClassLoader)、扩展类加载器(ExtensionClassLoader)、应用/系统类加载器(ApplicationClassLoader)
public class ClassLoaderTest {
public static void main(String[] args) {
ClassLoaderTest clt = new ClassLoaderTest();
Class clazz = clt.getClass();
System.out.println(clazz.getClassLoader()); //应用/系统类加载器
System.out.println(clazz.getClassLoader().getParent()); //扩展类加载器
System.out.println(clazz.getClassLoader().getParent().getParent()); //启动类加载器,由于不是由Java程序编写的,所以为null
}
}
//结果为
//sun.misc.Launcher$AppClassLoader@dad5dc
//sun.misc.Launcher$ExtClassLoader@7aac27
//null
类加载器的运行过程:

上图说明了类加载流程的同时也说明了双亲委派机制:加载类时,每一个类加载器都会将当前类传给上一级类加载器。当启动类加载器无法加载该类时,将类向下传递,由下级类加载器加载,当所有类加载器都无法找到当前类时,抛出异常
涉及Java类的加载过程:加载、验证、准备、解析、初始化

// object对象就是强引用
Object object = new Object();
Student stu = new Student();
stu.setName("张三");
WeakReference<Student> weakReference = new WeakReference<>(stu);
stu = null;
if (weakReference.get() != null){
System.out.println(weakReference.get());
}else {
System.out.println("对象为空");
}
//结果为:Student{name='张三'}
Student stu = new Student();
stu.setName("张三");
PhantomReference<Student> phantomReference = new PhantomReference<>(stu, new ReferenceQueue<Student>());
stu = null;
if (phantomReference.get() != null){
System.out.println(phantomReference.get());
}else {
System.out.println("对象为空");
}
//结果为:对象为空
public class FinalizeObject {
private static FinalizeObject finalizeObject;
public static void main(String[] args) {
finalizeObject = new FinalizeObject();
for (int i = 0; i <= 1; i++) {
System.out.println(String.format("--------------GC nums=%d--------------", i));
finalizeObject = null; //将finalizeObject对象置为“垃圾对象”
System.gc();//通知JVM运行GC
try {
Thread.sleep(100);//等待gc执行
} catch (InterruptedException e) {
e.printStackTrace();
}
if (finalizeObject == null){
System.out.println("finalizeObject is dead");
}else {
System.out.println("finalizeObject is still alive");
}
}
}
//finalize只会被调用一次,给正在被销毁的对象唯一一次复活的机会
@Override
protected void finalize() throws Throwable {
System.out.println("对象正在被销毁");
super.finalize();
finalizeObject = this;
}
}
//--------------GC nums=0--------------
//对象正在被销毁
//finalizeObject is still alive
//--------------GC nums=1--------------
//finalizeObject is dead
/**
* 槽位复用
* 对于变量a而言,其作用域仅在代码块之内
* 离开代码块后,即可判定其不再有用
* 这种时候,若在代码块外存在另一个变量b,
* 则变量b会复用变量a的槽位
*/
public void gc1() {
{
int[] a = {1,2,3};
System.out.println(a);
}
int b = 10;
System.out.println(b);
}

先尝试栈上分配,若不满足条件,尝试TLAB分配,若仍不满足,则在堆上分配。
栈上分配
TLAB
堆上分配
进行堆上分配时,对于不满足进入老年代的对象,将其放入Eden区
//未逃逸
public void hello(){
User user = new User();
user.setName("Alvis");
... ...
}
//逃逸
User user;
public void hello(){
user = new User();
user.setName("Alvis");
... ...
}
标量替换
桥接方法
桥接方法是在JVM加载期间对泛型进行类型擦除后,为指定了泛型类型参数的方法生成了一个中转方法;
如下代码,在Dog继承了Animal并指定泛型为String,实现其中eat(String s)方法,当JVM加载Dog和Animal时,会将Animal中的泛型进行类型擦除变成Object,那么Animal中的eat方法的参数将会变成Object类型,则JVM会在Dog类中使用桥接方法将该方法实现。
import java.lang.reflect.Method;
public interface Animal<T> {
public void eat(T s);
//类型擦除后的方法
//public void eat(Object s);
}
class Dog implements Animal<String>{
@Override
public void eat(String s) {
System.out.println("dog eat " + s);
}
//类型擦除后实现的方法(桥接方法)
//@Override
//public void eat(Object s) {
// eat((String) s);
//}
//测试用的main方法
public static void main(String[] args) {
Dog dog = new Dog();
Method[] declaredMethods = dog.getClass().getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
if (declaredMethod.getName().equals("eat")){
System.out.print(declaredMethod.getName() + " ");
for (Class<?> parameterType : declaredMethod.getParameterTypes()) {
System.out.print(parameterType.getName());
}
System.out.println("\n=================================");
}
}
}
}
//输出结果
//eat java.lang.String
//=================================
//eat java.lang.Object
//=================================
无法处理循环引用的情况

引用计数器要求在每次因引用产生和消除的时候,需要伴随一个加减法操作,对系统性能有一定影响。




1、串行垃圾回收器(SerialGC)



4、G1垃圾回收器
深入理解Java虚拟机[第三版]
类加载器