0%

java | 类加载内存分析

从内存的层次分析类。

  • JAVA 内存
      • 存放 new 的对象和数组
      • 可以被所有的线程共享,不会存放别的对象引用
      • 存放基本变量类型(会包含这个基本类型的具体数值)
      • 引用对象的变量(会存放这个引用在对里面的具体地址)
    • 方法区
      • 可以被所有线程共享
      • 包含了所有的 classstatic 变量

类的加载过程

  • load
    • 将类的 class 文件读入内存,并为之创建一个 java.lang.Class 对象。此过程由类加载器完成
  • link
    • 将类的二进制数据合并到 JRE
    • 正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存将在方法区中进行分配
    • 虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程
  • Initialize 初始化
    • JVM 负责对类进行初始化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.redisc;

class A {
static int m = 100;

static {
System.out.println("123");
m = 300;
}

public A() {
System.out.println("A");
}
}


public class Test {

public static void main(String[] args) throws ClassNotFoundException {
A a = new A();
System.out.println(a.m);
}
}

输出

1
2
3
123
A
300

之所以 m 最后输出的是 300 是因为,代码块的顺序在下面。

如果顺序是

1
2
3
4
5
6
static {
System.out.println("123");
m = 300;
}

static int m = 100;

m 的输出为 100.

什么时候类初始化

  • 类的主动引用(一定会发生类的初始化)
    • 当虚拟机启动,先初始化 main 方法所在的类
    • new 一个类的对象
    • 调用类的成员变量(除 final 变量)和静态方法
    • 使用 java.lang.reflect 包的方法对类进行反射调用
    • 初始化一个类,如果父类没有被初始化,则会先初始化它的父类
  • 类的被动引用(不会发生类的初始化)
    • 当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:通过字类引用父类的静态变量,不会导致字类初始化
    • 通过数组定义类引用,不会出发此类的初始化
    • 引用敞亮不会出发此类的初始化
请我喝杯咖啡吧~