java 相关

158 阅读2分钟

jvm 只是一个软件,并不是操作系统,所以 jvm 并不具备操作内存的权限,它想使用内存也必须通过 alloc 等方法向系统申请。所以常说的 jvm 中的堆并不等价于进程角度的堆(堆、栈、text 段、mmap 段等)

jvm 堆的创建 中可以看出,jvm 的堆只是通过 mmap 预分配的一块大内存而已

获取实际泛型类型

java 中泛型可擦除,所以一般情况下我们拿不到泛型的实际类型,但 gson 通过子类方式获取到实际类型。其原理是利用了class 文件的 signature 属性,Signature 是 class 文件属性表中的一项。 假设有源文件如下

public class Test111<T>{
    public static void main(String[] args) {
        // 注意后面跟了 {},表示创建 Test111 的子类对象,而不是 Test111 对象
        Test111<String> t1 = new Test111<String>(){};
    }
}

在 main 函数中,通过加上 {} 在声明 Test111 子类的同时创建对象

因为定义了一个子类,所以 javac 在编译时会为子类单独生成一个 class 文件,通过 javap -v 反编译该 class 文件以及 Test111 对应的 class 文件,如下:

// #9 指的是字符串常量池中第 9 项,其值为 // 后面的内容,从中可以看出泛型实际类型
// 如果子类定义有多个泛型,这里也会列出所有的泛型

Signature: #9                           // Lcom/sogle/lib/Test111<Ljava/lang/String;>;

// Test111 对应的 class 文件
// 这里可以看出 class 文件中只存储了 T,并没有指明它的实际类型
Signature: #14                          // <T:Ljava/lang/Object;>Ljava/lang/Object;

如果说类并没有使用泛型,反编译后的内容并不会存在 Signature 属性值,该属性也主要是为了弥补在反射时无法获取泛型信息而添加的

动态代理

内部动态生成 class 文件(只存在于内存中不,不落于文件),然后通过反射生成该 class 的实例

  1. 通过 ProxyGenerator#generateProxyClass() 生成 class 文件内容

    // 生成 class 文件内容
    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
        proxyName, interfaces, accessFlags);
    try {
        // 由 class 内容生成 Class 对象
        return defineClass0(loader, proxyName,
                            proxyClassFile, 0, proxyClassFile.length);
    } catch (ClassFormatError e) {
        throw new IllegalArgumentException(e.toString());
    }
    
  2. 由 class 对象调用到 InvocationHandler 的逻辑非常简单:重写相应方法,然后直接调用

    image.png

  3. 生成的 class 文件可断点 ProxyGenerator#generateProxyClass() 方法断点查看地址 image.png

    上图中 saveGeneratedFiles 可通过下面代码重新设置

    // 有人说新版本需要设置这个
    System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
    System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");