前言
基于 JDK 8
类文件格式
Java 之所以可以“一次编译,到处运行”,一是因为 JVM 针对各种操作系统、平台都进行了定制;二是因为无论任何平台,javac
编译生成固定格式的字节码文件(*.class)供 JVM 使用。
格式:
- 魔数(Magic)
- 版本号(Version)
- 常量池(constant_pool)
- 访问标志(access_flag)
- 当前类索引(this_class)
- 父类索引(super_class)
- 接口索引(interfaces)
- 字段表(fields)
- 方发表(methods)
- 附加属性(attributes)
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count - 1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
类加载器
# 监控类的加载
-XX:+TraceClassLoading
-XX:+TraceClassUnloading
要执行 class 文件就需要先将其读入内存,这个是由类加载器完成的。
类加载器类型:
名称 | 说明 |
---|---|
Bootstrap ClassLoader | C++ 实现,无法访问 |
Extension ClassLoader | 父类为 Bootstrap,显示为 null |
Application ClassLoader | 父类为 Extension |
自定义 ClassLoader | 父类为 Application |
package xianzhan.jdk8;
/**
* @author xianzhan
* @since 2022-06-11
*/
public class Main {
public static void main(String[] args) {
LeeClassLoader loader = new LeeClassLoader();
System.out.println(loader.getClass().getName());
ClassLoader p = loader.getParent();
while (p != null) {
System.out.println(p.getClass().getName());
p = p.getParent();
}
}
}
class LeeClassLoader extends ClassLoader {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
System.out.println("重写该方法. name: " + name);
return super.findClass(name);
}
}
运行时数据区
# 设置线程栈大小,限制栈的深度
-Xss1M
# 逃逸分析
# 开启逃逸分析 JDK 1.6 默认开启
-XX:+DoEscapeAnalysis
# 查看逃逸分析的筛选结果
-XX:+PrintEscapeAnalysis
# 元空间
# 设置分配的 Metaspace 的大小,Metaspace 将在首次超过垃圾收集时触发垃圾收集。
# 垃圾收集的阈值取决于使用的元数据量而增加或减少。默认大小取决于平台。
-XX:MetaspaceSize=size
# 设置可以分配给 Metaspace 的最大本机内存。默认情况下,大小不受限制。
# 应用程序的 Metaspace 量取决于应用程序本身,其他正在运行的应用程序以及系统上可用的内存量
-XX:MaxMetaspaceSize=size
# 堆
# JVM 启动时, 所占据的操作系统内存的大小
-Xms2G
-XX:InitialHeapSize=2G
# 最大堆是指新生代和老年代的大小之和的最大值
# 它是 Java 应用程序的堆上限
-Xmx2G
-XX:MaxHeapSize=2G
# 用于设置新生代的大小
# 新生代越大, 老年代越小
# 新生代一般设置为整个堆空间的 1/4 到 1/3 左右
-Xmn512M
# -Xmn 的效果等同于设置了相同的 -XX:NewSize 和 -XX:MaxNewSize
# 下面参数不一致时, 可能会导致内存震荡, 从而导致不必要的开销
# 设置新生代的初始大小
-XX:NewSize
# 设置新生代的最大值
-XX:MaxNewSize
# 设置新生代中, eden 空间和 s0 空间的比例关系
# -XX:SurvivorRatio=eden/s0=eden/s1
-XX:SurvivorRatio=8
# 设置 survivior 区的可使用率
# 当 survivior 区的空间使用率达到这个数值时, 会将对象送入老年代
-XX:TargetSurvivorRatio
# 设置新生代和老年代的比例
# -XX:NewRatio=老年代/新生代
-XX:NewRatio=2
# 设置堆空间的最小空闲比例
# 当堆空间的空闲内存小于这个数值时, JVM 便会扩展堆空间
-XX:MinHeapFreeRatio
# 设置堆空间的最大空闲比例
-XX:MaxHeapFreeRatio
java
Run-Time Data Areas
JEP 122: Remove the Permanent Generation
JEP 147: Reduce Class Metadata Footprint
JEP 149: Reduce Core-Library Memory Usage
JDK 8 运行时数据区里的方法区,由之前的永久代(Permanent Generation)改为元空间(Metaspace)。