Jvm对象创建和类加载过程

程序员 2024-10-7 13:42:18 131 0 来自 中国
1.对象创建流程是怎样的?有哪些步调,分别有什么作用?

        jvm创建对象重要颠末类加载查抄、分配内存、初始化、设置对象头、实行初始化方法这几个阶段,下面将渐渐剖析每一步的寄义。
类加载查抄
        起首第一步是类加载查抄,当假造机会到new指令时,起首查抄这个指令的参数可否在常量池中定位一个类的符号引用,并查抄这个类是否已经加载、剖析、初始化过,假如没有,就实行类加载流程
分配内存
        实际上为对象分配内存就是从堆中分割一块空间给当前对象。
        在第二步分配内存时,我们要关心两个标题:
         1.内存分配的方式是怎样的?
         2.并发场景下,jvm怎么保证内存分配的原子性?
        jvm内存分配方式有如下两种:
        1.指针碰撞(Bump the Pointer):假如java堆中的内存都是规整的,已利用的内存在一边,未利用的内存在另一边,中央用指针举行分割,当要给对象分配内存时,只必要将指针按对象巨细移动到未利用内存那边,那么内存就分配完毕。
        2.空闲列表(Free List):假如java堆中的内存,已利用的和未利用的内存交织在一起,那么假造机就要维护一个可用内存列表,当要分配内存时,就查询这个列表上的可用内存举行分配。
        办理并发分配内存:
        1.假造机利用了CAS(compare and swap)配上失败重试的方式来保证更新操纵的原子性
        2.本地线程分配缓冲(Thread Local Allocation Buffer,TLAB):将内存分配的操纵按线程分别在差别的空间中举行,即每个线程都预先从堆中分配到一块独有的内存空间,当本地线程的缓冲空间利用完,才会利用CAS去Eden区竞争内存。假造机是否利用TLAB,可以通过-XX:+UseTLAB这个参数来设置。
初始化
        这里就是给对象的字段举行初始化零值,假如利用了TLAB,也可以提前到TLAB中举行。
设置对象头
        对象在jvm中是有三部分构成,分别是对象头(Object Header)、实例数据(Instance Data)、对齐添补(Padding)。
        在这一步重要是设置对象运行时的数据,比如哈希码、GC分代年岁、锁状态标志位、线程持有的锁、方向线程ID、方向时间戳等。对象头尚有一部分是范例指针(klass point),指向的是类的元数据信息,也就是当前对象是哪个类的实例,假如是数组还必要设置数组的长度。
实行<init>()方法
        在这一步是按照我们开发职员指定的值给属性举行初始化,实行完这个方法后,对象才是真正的创建完成。
2.类加载流程是怎样的?

        我们编写好的代码,通常必要编译成class文件,然后加载到内存中才能被假造机辨认和利用。将一个class文件加载到内存中,可以从两方面举行相识,起首就是类加载的过程,其次就是jvm的类加载器,那么下面将从这两方面举行详细的先容。
    2.1 类加载的流程

2.png         类加载重要有加载、验证、准备、剖析、初始化、利用、卸载这七个步调。
        加载
        在类加载阶段会完成三件事:
        1.通过类的全限定名,来获取这个类的二进制字节流
        2.将读取到的字节流存放到运行时数据区的方法区
        3.在堆中天生一个对应这个类的对象,用于访问方法区这个类的入口普通点说,就是将class文件存放到方法区中,然后在堆内里天生一个该类的java.lang.Class对象,用来访问类的信息
        验证
        这个阶段重要验证字节码的格式是否符合规范
        准备
        这个阶段重要是给静态变量分配内存、设置初始零值(int范例赋值0)。但是这里有个特殊的地方,就是在给final修饰的字段举行赋值时,是赋予指定的值。
根本范例的默认零值:
3.png
// final修饰的静态字段在准备阶段会赋指定的值
public static final int value = 111
        剖析
        假造机将常量池中的符号引用替换为直接引用。
        符号引用:用一组符号来形貌所引用的对象,通过符号可以定位指定的数据。
        直接引用:可以是直接指向对象的指针、内存相对偏移量。
        静态链接:在类加载过程将符号引用替换为直接引用,就叫静态链接。
        动态链接:在步调运行时,将符号引用天魂成直接引用,就叫动态链接。
        初始化
        在当前阶段,则会根据开发职员指定的值去给字段举行赋值,然后实行静态代码块。
        2.2 类加载器


          类加载器的重要作用:通过一个类的全限定名来获取该类的二进制字节流,实现这个过程的就是类加载器(Class Loader)。
           Jvm中有三种类加载,分别是启动类加载器(Bootstrap Class Loader)、扩展类加载器(Extension Class Loader)、应用步调类加载器(Application Class Loader),此中启动类加载器是C++实现,因此启动类加载器不能在java中获取。
         启动类加载器(Bootstrap Class Loader):负责加载\lib目次下的核心类库。
         扩展类加载器(Extension Class Loader):负责加载\lib\ext目次下的扩展类库。
         应用步调类加载器(Application Class Loader):负责加载用户类路径(classpath)下的全部类库。
        java应用步调都是由这三种类加载器相互共同来实现类的加载,它们直接的关系如下图所示:
       这种工作模式被称为类加载器双亲委派机制,它们的实行流程是:当某一个类要被加载时,当前类的加载器会先查抄自己是否已经加载过这个类,假如没有加载过,就会委派给父类加载器(直到最顶级的类加载器),假如父类加载器加载失败,那么子加载器才会去加载该类。
    双亲委派的长处:
    1.沙箱安全机制,制止核心的类库被窜改,假如我们自己也界说一个String类,那么类加载器是不会加载的。
     2.制止重复加载类似的类。
     类和类加载器的关系:在java中,同一个类被差别的类加载器加载,那么这两个类也不类似。
3.对象内存分配

      对象内存分配重要流程如下图所示:
5.png         对象栈上分配:
        大多数对象都是分配在堆上,但是假如某些对象颠末“逃逸分析”,发现不会发生逃逸征象,该对象则会在栈上举行分配。假如栈上的一连空间不敷以放下当前对象,那么jvm会利用“标量替换”,将对象举行拆分后存放栈内。对象分配在栈上,可以随着栈帧的出栈而烧毁,减轻垃圾接纳的压力。
        逃逸分析:该对象只作用在本方法内,无法被外部访问到,就代表没有逃逸,相反,假如可以被外部访问,则是逃逸。利用XX:+DoEscapeAnalysis(开启逃逸分析)-XX:- DoEscapeAnalysis(关闭逃逸分析)。
        标量替换:将一个聚合量(java中的对象)拆分成多少份,根据步调访问,将用到的成员变量规复为原始范例,这就是标量替换。利用-XX:+EliminateAllocations(开启标量替换)-XX:-EliminateAllocations(关闭标量替换)。
// test1中user逃逸出test1方法
public User test1() {
User user = new User();
  user.setId(1);
  user.setName("test");
  return user;
}
// test2中user没有逃逸出去
public void test2() {
    User user = new User();
    user.setId(1);
    user.setName("test");
}
        对象Eden区分配:
        大多数情况下,对象都是在Eden区举行分配,假如Eden区内存不敷,假造时机实行MinorGc。如上图所示,年轻代分为Eden区、Survivor0、Survivor1区,它们之间占比为8:1:1。
        大对象直接进入老年代:
        大对象指的是一连占用大量内存空间的java对象,比如数组。假造机提供了-XXretenureSizeThreshold参数,可以设置对象到达某个阈值后,直接进入老年代中,如许做的长处是制止了大对象在年轻代中来回的复制。这-XXretenureSizeThreshold参数只在Serial和ParNew两款年轻代网络器下有用。
        恒久存活的对象进入老年代:
        假造机给每个对象的头部都设置了一个Gc分代年岁计数器,每履历过一次Gc,计数器就会+1,默认是到达15次后,就会提升到老年代中。可以利用-XX:MaxTenuringThreshold=15参数来举行设置。
        对象动态年岁判定:
        一批对象的总巨细大于当前Survivor区的50%,那么大于即是这批对象年岁的对象,直接进入老年代中。比方当前Survivor区有一批对象,年岁1+2+3+n大于当前Survivor区的50%,此时就会将大于即是n的对象,直接转移到老年代中。对象动态年岁判定一样平常在minor Gc实行后触发。
        空间分配包管:
        在实行minor Gc前,假造时机先查抄老年代剩余可用空间是否大于新生代全部对象的巨细,假如大于就实行minor Gc;否则,假造机将查抄是否设置了包管参数“-XX:HandlePromotionFailure”,假如有包管参数,就会判定老年代剩余可用空间是否大于历次提升到老年代对象的匀称巨细,假如不符合就实行full Gc,符合就实行minor Gc。
6.png
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2025-2-6 05:41, Processed in 0.106211 second(s), 35 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表