JVM入门
1 JVN入门探究
- 谈谈对JVM的理解?java8虚拟机和之前的变化更新?
- OOM内存溢出,StackOverFlowError栈溢出,怎么分析?
- JVM 的常用调优参数有哪些?
- 内存快照如何抓取,怎么分析Dump文件?
- 谈谈JVM中,类加载器你的认识?rt-jar ext applicatoin
2 JVM的体系研究
2.1 JVM位置
2.2 JVM的体系结构
- 栈百分百没有垃圾(因为栈的运行方式,不存会在垃圾)
- 所谓的 JVM 调优,99%都是在调方法区和堆,其中主要的是堆!
- 第三方插件,主要是在执行引擎上做,类加载器上比较少
2. 类加载器
**作用:**加载Class文件
- 类是抽象的,对象是具体的
- 对象的引用地址在栈,具体数据在堆
- 第三方插件,主要是在执行引擎上做,类加载器上比较少
2.1 理解类加载器:
class Test{
public static int a = 1;
}
//我们程序中给定的是 public static int a = 1;
但是在加载过程中的步骤如下:
- 加载阶段 编译文件为 .class文件,然后通过类加载,加载到JVM
- 连接阶段
第一步(验证):确保Class类文件没问题
第二步(准备):先初始化为 a=0。(因为你int类型的初始值为0)
第三步(解析):将引用转换为直接引用 - 初始化阶段: 通过此解析阶段,把1赋值为变量a
输出都为同一个类:
public class Car {
public static void main(String[] args) {
Car car1 = new Car();
Car car2 = new Car();
Car car3 = new Car();
Class<? extends Car> aClass = car1.getClass();
Class<? extends Car> aClass1 = car2.getClass();
Class<? extends Car> aClass2 = car3.getClass();
System.out.println(aClass);
System.out.println(aClass2);
System.out.println(aClass2);
}
}
Class Car
2.2 类加载器的执行顺序
类的加载指的是将类的.class文件中二进制数据读入到内存中,将其放在运行时数据区内的方法区内,然后再内存中创建一个 java.lang.Class 对象用来封装类在方法区内的数据结构。
- 对于静态字段来说,只有直接定义了该字段的类才会被初始化;
- 当一个类在初始化时,要求其父类全部都已经初始化完毕了;
- 所有Java虚拟机实现必须在每个类或者接口被Java程序“首次主动使用”时才初始化他们
public class Ltest {
public static void main(String[] args) {
System.out.println(Child.str2);
}
}
class MyParent{
public static String str1 = "hello";
static {
System.out.println("MyParent static");
}
}
class Child extends MyParent{
public static String str2 = "world";
static {
System.out.println("Child static");
}
}
输出:
MyParent static
Child static
world
2.3 常量池的概念
常量在编译阶段会存入到调用这个常量的方法所在的类的常量池中 本质上,调用类并没有直接用用到定义常量的类,因此并不会触发定义常量的类的初始化。 注意:这里指的是将常量存放到了MyTest2的常量池中,之后MyTest2与MyParent2就没有任何关系 了。
public class Ltest2 {
public static void main(String[] args) {
System.out.println(MyParent2.str3);
}
}
class MyParent2{
public static final String str3 = "qwe";
static {
System.out.println("MyParent2 static");
}
}
qwe
当一个常量的值并非编译期间可以确定的,那么其值就不会被放到调用类的常量池中, 这是在程序运行时,会导致主动使用这个常量所在的类,显然就会导致这个类被初始化。
import java.util.UUID;
public class Ltest2 {
public static void main(String[] args) {
System.out.println(MyParent2.str3);
}
}
class MyParent2{
public static final String str3 = UUID.randomUUID().toString();
static {
System.out.println("MyParent2 static");
}
}
因为这个例子的值,是只有当运行期才会被确定的值,所以需要运行static方法。而上一个例子的值,是编译时就能被确定的值。