JVM的体系架构图
JVM的调优就是调堆heap!
JVM中,栈、本地方法栈和程序计数器一定不会有垃圾!
类加载器
类加载到JVM的过程。
类的加载、连接和初始化
- 加载:查找并加载类的二进制数据
- 连接
- 验证:保证被加载类的正确性,保证编译通过。
- 准备:给类的
静态变量
分配内存空间,并给该变量赋值初始默认值。 - 解析:把类中的符号引用转换为直接引用,即将静态变量指向分配的内存地址。
- 初始化:给类的静态变量赋值正确的值。
public class Test{
public static int a = 1;
}
// 1、加载:编译文件为.class文件,通过classLoader,加载到JVM中。
// 2、连接
// a、验证:保证Class类文件没有问题
// b、准备:分配一块内存空间,存储int类型的数据,并赋值一个默认值0,即a = 0
// c、解析:符号引用转换为直接引用
// 3、初始化:把1赋值给a
类的加载-static
// JVM的参数:-XX:+TraceClassLoading //用于追踪类的加载信息,并将加载类和顺序打印出来
public class Demo01 {
public static void main(String[] args) {
System.out.println(Child01.str2);
/* 执行结果:
* parent static
* child static
* hello child
*/
}
}
class Parent01 {
public static String str = "hello parent";
static {
System.out.println("parent static");
}
}
class Child01 extends Parent01 {
public static String str2 = "hello child";
static {
System.out.println("child static");
}
}
静态代码块在类的初始化阶段就执行了,按照先父类后子类的顺序执行。
常量-final
public class Demo02 {
public static void main(String[] args) {
System.out.println(Parent02.STR);
/* 输出结果:
* hello parent
*/
}
}
class Parent02 {
public static final String STR = "hello parent";
static {
System.out.println("parent static");
}
}
常量(static final修饰)在编译阶段
就放到常量池
中了,上面代码表示将常量放到了Demo02的常量池中,之后Demo02和Parent02就没有关系了。
public class Demo03 {
public static void main(String[] args) {
System.out.println(Parent03.STR);
/* 执行结果:
* parent static
* 48298a3f-63ee-49a6-b836-ab6e4ecea6cf
*/
}
}
class Parent03 {
public static final String STR = UUID.randomUUID().toString();
static {
System.out.println("parent static");
}
}
当一个常量的值不能在编译期间确定时,那么这个值就不会放在常量池中!程序在运行期间,会使用常量所在的类。