堆
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 的实例
-
通过 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()); } -
由 class 对象调用到 InvocationHandler 的逻辑非常简单:重写相应方法,然后直接调用
-
生成的 class 文件可断点 ProxyGenerator#generateProxyClass() 方法断点查看地址
上图中 saveGeneratedFiles 可通过下面代码重新设置
// 有人说新版本需要设置这个 System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true"); System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");