java类加载机制

类加载流程

双亲委派模型:
Bootsrap类加载器,负责加载JAVA_HOME/lib下的类或者被-Xbootclasspath 参数所指定的路径种的,并且是虚拟机识别的(仅按照文件名识别,如rt.jar,名字不符合的类库即使放在lib目录下也不会重载)
ExtentClassLoad扩展类加载器,负责加载JAVA_HOME/lib/ext下的类或者由系统路径获取java.ext.dirs系统变量直接指定的路径下的类库,开发者可以直接使用
Application ClassLoad应用类加载器,负责加载Class path下的类。
ClassLoader. getSystemClassLoader返回值即为该类加载器

加载
根据一个类的全限定名(如cn.edu.hdu.test.HelloWorld.class)来读取此类的二进制字节流到JVM内部;
将字节流所代表的静态存储结构转换为方法区的运行时数据结构(hotspot选择将Class对象存储在方法区中,Java虚拟机规范并没有明确要求一定要存储在方法区或堆区中)
转换为一个与目标类型对应的java.lang.Class对象;

连接又分为验证、准备、解析阶段:
验证
验证阶段主要包括四个检验过程:文件格式验证、元数据验证、字节码验证和符号引用验证;

准备
为类中的所有静态变量分配内存空间,并为其设置一个初始值(由于还没有产生对象,实例变量将不再此操作范围内);

解析
将常量池中所有的符号引用转为直接引用(得到类或者字段、方法在内存中的指针或者偏移量,以便直接调用该方法)。这个阶段可以在初始化之后再执行。

初始化
在连接的准备阶段,类变量已赋过一次系统要求的初始值,而在初始化阶段,则是根据程序员自己写的逻辑去初始化类变量和其他资源,举个例子如下:

public static int value1  = 5;
public static int value2  = 6;
static{
    value2 = 66;
}

在准备阶段value1和value2都等于0;

在初始化阶段value1和value2分别等于5和66;

所有类变量初始化语句和静态代码块都会在编译时被前端编译器放在收集器里头,存放到一个特殊的方法中,这个方法就是<clinit>方法,即类/接口初始化方法,该方法只能在类加载的过程中由JVM调用;
编译器收集的顺序是由语句在源文件中出现的顺序所决定的,静态语句块中只能访问到定义在静态语句块之前的变量;
如果超类还没有被初始化,那么优先对超类初始化,但在<clinit>方法内部不会显示调用超类的<clinit>方法,由JVM负责保证一个类的<clinit>方法执行之前,它的超类<clinit>方法已经被执行。
JVM必须确保一个类在初始化的过程中,如果是多线程需要同时初始化它,仅仅只能允许其中一个线程对其执行初始化操作,其余线程必须等待,只有在活动线程执行完对类的初始化操作之后,才会通知正在等待的其他线程。(所以可以利用静态内部类实现线程安全的单例模式)
如果一个类没有声明任何的类变量,也没有静态代码块,那么可以没有类<clinit>方法;

1.准备阶段为类变量(static修饰的变量,非类实例变量)分配内存并设置初始值,这些变量使用的内存都在方法区中进行分配(实例变量将会在对象初始化时分配在堆中)
public static value=123,在准备阶段会设置为0,在初始化阶段再设置为123
public static final value=123为特殊情况,在准备阶段就会初始化为123
2.静态代码块和实例变量都是在初始化阶段赋值

public static int value=5;
public static int value2=6;
static {
value2=66;
}
public class SSClass {
    static {
        System.out.println("SSClass init...");
    }
}
public class SuperClass extends SSClass {
    static {
        System.out.println("SuperClass init...");
    }
    public static int value=123;
    public SuperClass(){
        System.out.println("init SuperClass");
    }
}
public class SubClass extends SuperClass {
    static {
        System.out.println("SubClass init...");
    }
    static int a;
    public SubClass(){
        System.out.println("init SubClass..");
    }
}
public class NotInitialization {
    public static void main(String[] args) {
        System.out.println(SuperClass.value);
    }
}
------------------------------------
结果:
SSClass init...
SuperClass init...
123

1.只会初始化定义字段的类
2.对final static修饰的字段引用,不会初始化所在类(在准备阶段已经赋值,并放在了常量池)

public class ConstantClass {
    static {
        System.out.println("ConstantClass init");
    }
    public final static String HELLOWORLD="hello world";
}
public class NotInitialization {
    public static void main(String[] args) {
        System.out.println(ConstantClass.HELLOWORLD);
    }
}
----------------------
结果
hello world

3.通过数组定义来引用类,不会触发此类的初始化

public class NotInitialization
{
    public static void main(String[] args)
    {
        SuperClass[] sca = new SuperClass[10];
    }
}

jvm有严格的规定(五种情况):
1.遇到new,getstatic,putstatic,invokestatic这4条字节码指令时,假如类还没进行初始化,则马上对其进行初始化工作。
其实就是3种情况:用new实例化一个类时、读取或者设置类的静态字段时(不包括被final修饰的静态字段,因为他们已经被塞进常量池了)、以及执行静态方法的时候。
2.使用java.lang.reflect.*的方法对类进行反射调用的时候,
如果类还没有进行过初始化,马上对其进行。
3.初始化一个类的时候,如果他的父亲还没有被初始化,则先去初始化其父亲。
4.当jvm启动时,用户需要指定一个要执行的主类(包含static void main(String[] args)的那个类),则jvm会先去初始化这个类。
5.用Class.forName(String className);来加载类的时候,也会执行初始化动作。
注意:ClassLoader的loadClass(String className);方法只会加载并编译某类,并不会对其执行初始化。


利用类的实例化构建一个单例模式:

public class Singleton{
private static Singleton singleton=new Singleton();
Singleton(){
}
//静态方法获取私有属性,如果类没有初始化,则去初始化,初始化时,jvm会保证只有一个线程可以去执行初始化操作
public static Singleton getInstance(){
   return singleton;
}

double check的单例模式:
类属性(static修饰)在准备阶段会赋值为null (int 型等会分配内存,赋初值0),在初始化阶段进行初始化,分配内存进行初始化,并将singleton指针指向,不具有原子性,所以需要加锁.
1.volatile的作用:防止内存重排序
new对象的过程:
a.分配内存
b.赋初值
c.指针singleton指向内存
指令重排序后导致可能执行下列的步骤:
a.分配内存
b.指针singleton指向内存
c.内存初始化
synchronized加锁虽然能保证互斥,但是不保证程序在一个时间片内将所有代码执行完毕,会释放时间片,等待在分配时间片再执行。如果b和c执行顺序被调换,线程1分配了内存,并将singleton指向了内存,但是却没有初始化,此时正好写会了主存。此时线程2执行,在第一个if(singleton==null) (此处并没有加锁)进行判断,返现singleton并不为null,则返回了没有初始化的singleton,导致引用到的地方发生奔溃。

public class Singleton{
  private static volatile Singleton singleton=null;
  public  static Singleton getInstance(){
    if(singleton==null){
      synchronized(this){ 
 //加锁后,线程执行完会将singleton刷新到主存中,但是!!
//如果此时线程时间片用完了,但是只执行了分配内存、指针singleton指向
//内存,却没有执行初始化!!下一次线程判断不为null之后,不会执行新建,会
//用到没有初始化的内存块。程序可能会奔溃
           if(singleton==null){
              singleton=new Singleton();
          }         
        }
     }
  }
  return singleton;
}

【参考博客】
https://www.cnblogs.com/aspirant/p/7200523.html
https://blog.csdn.net/noaman_wgs/article/details/74489549
https://blog.csdn.net/anjxue/article/details/51038466
https://www.cnblogs.com/aspirant/p/7200523.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352

推荐阅读更多精彩内容