您好,欢迎访问代理记账网站
  • 价格透明
  • 信息保密
  • 进度掌控
  • 售后无忧

jvm对象

1.对象的创建

对象的创建大概可以分为5步,流程如下图所示

1.类加载检查

虚拟机遇到一条new指令时,首先在常量池中定位到这个类的符号引用,然后检查这个符号引用代表的类是否已经被加载过、解析和初始化过,如果没有,那必须先执行相应的类加载过程。

2.分配内存

就是在类加载完成之后,为对象分配实际的内存,对象的大小会在类加载的时候知道,分配内存就是把一块确定大小的内存从Java堆里分出来。有“指针碰撞”和“空闲列表”两种方式。

  • 内存分配并发问题

    在创建对象的时候有一个很重要的问题,就是线程安全,因为在实际开发过程中,创建对象是很频繁的事情,作为虚拟机来说,必须要保证线程是安全的,通常来讲,虚拟机有两种方式来保证线程安全:

    CAS+失败重试:CAS是乐观锁的一种实现方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。虚拟机采用CAS配上失败重试的方式保证更新操作的原子性。

    TLAB:为每一个线程预先在Eden区分配一块儿内存,JVM在给线程中的对象分配内存时,首先在TLAB分配,当对象大于TLAB中剩余内存或TLAB的内存已用尽时,再采用上述的CAS进行内存分配。

3.初始化零值

内存分配完成后,虚拟机要将分配的内存都初始化为零值(不包括对象头),就是为了保证对象的实例字段能够在不赋初始值时可以直接使用。

4.设置对象头

初始化零值后,虚拟机需要对对象头进行设置,比方说这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息。这些信息存放在对象头中。另外,根据虚拟机当前运行状态的不同,如是否启动偏向锁等,对象头会有不同的设置方式。

5.执行init方法

设置完对象头之后,对于虚拟机来讲,对象就已经创建完成了,但对Java程序来说才刚刚开始,方法还没有执行,所有的字段都还为零。所以一般new指令完成之后都会执行方法,按照程序所写的意思进行初始化,这样一个真正可用的对象才算完全产生。

在这里插入图片描述

2.对象在内存中的结构

这里写图片描述

参考:https://www.cnblogs.com/duanxz/p/4967042.html

3. 堆栈方法区的交互

Java程序会通过栈上的引用(reference)数据来操作堆上的具体对象。

对象的访问定位目前有两种方式,一种是通过句柄访问,一种是直接指针

句柄:如果通过句柄访问的话,那么java堆中将会划分出一块内存来作为句柄池,引用(reference)中存储的就是对象的句柄池,句柄池中包含对象实例数据和对象类型数据的指针。

**直接指针:**如果使用直接指针访问,reference中存储的直接就是对象实例数据,对象实例数据中有指向对象类型的指针。

**两种方式对比:**使用句柄来访问的最大好处就是reference中存储的是稳定的句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中的实例数据指针,而reference本身不需要修改。

使用直接指针访问方式的最大好处就是速度更快,它节省了一次指针定位的时间开销,由于对象的访问在java中非常频繁,因此这类开销积少成多后也是一项十分可观的执行成本。

这里写图片描述

4.内存分配

为对象分配空间的任务等同于把一块确定大小的内存从Java堆中划分出来。

4.1 给对象分配内存方式

4.1.1 指针碰撞(Serial、ParNew等带Compact过程的收集器)

假设Java堆中内存是绝对规整的,所有用过的内存都放在一边,空闲的内存放在另一边,中间放着一个指针作为分界点的指示器,那所分配内存就仅仅是把那个指针向空闲空间那边挪动一段与对象大小相等的距离,这种分配方式称为“指针碰撞”(Bump the Pointer)。

4.1.2 空闲列表(CMS这种基于Mark-Sweep算法的收集器)

如果Java堆中的内存并不是规整的,已使用的内存和空闲的内存相互交错,那就没有办法简单地进行指针碰撞了,虚拟机就必须维护一个列表,记录上哪些内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录,这种分配方式称为“空闲列表”(Free List)。

4.2 内存分配流程

总体流程
这里写图片描述

对象分配流程
这里写图片描述
  如果开启栈上分配,JVM会先进行栈上分配,如果没有开启栈上分配或则不符合条件的则会进行TLAB分配,如果TLAB分配不成功,再尝试在eden区分配,如果对象满足了直接进入老年代的条件,那就直接分配在老年代。

4.3 对象分配在堆栈

在学习Java的过程中,一般认为new出来的对象都是被分配在堆上的,其实这个结论不完全正确,因为是大部分new出来的对象被分配在堆上,而不是全部。通过对Java对象分配的过程分析,可以知道有另外两个地方也是可以存放对象的。这两个地方分别栈 (涉及逃逸分析相关知识)和TLAB(Thread Local Allocation Buffer)。

4.3.1 栈上分配

在JVM中,堆是线程共享的,因此堆上的对象对于各个线程都是共享和可见的,只要持有对象的引用,就可以访问堆中存储的对象数据。虚拟机的垃圾收集系统可以回收堆中不再使用的对象,但对于垃圾收集器来说,无论筛选可回收对象,还是回收和整理内存都需要耗费时间。

如果确定一个对象的作用域不会逃逸出方法之外,那可以将这个对象分配在栈上,这样,对象所占用的内存空间就可以随栈帧出栈而销毁。在一般应用中,不会逃逸的局部对象所占的比例很大,如果能使用栈上分配,那大量的对象就会随着方法的结束而自动销毁了,无须通过垃圾收集器回收,可以减小垃圾收集器的负载。

JVM允许将线程私有的对象打散分配在栈上,而不是分配在堆上。分配在栈上的好处是可以在函数调用结束后自行销毁,而不需要垃圾回收器的介入,从而提高系统性能。

1) 栈上分配如何开启

栈上分配前提:

  • 开启逃逸分析 (-XX:+DoEscapeAnalysis);逃逸分析的作用就是分析对象的作用域是否会逃逸出方法之外,再server虚拟机模式下才可以开启(jdk1.6默认开启)
  • 开启标量替换 (-XX:+EliminateAllocations);标量替换的作用是允许将对象根据属性打散后分配再栈上,默认该配置为开启

查看逃逸结果:可以通过选项-XX:+PrintEscapeAnalysis查看逃逸分析的筛选结果。

逃逸分析的基本行为就是分析对象的动态作用域:当一个对象在方法中被定义后,它可能被外部方法所引用。

方法逃逸:例如作为调用参数传递到其他方法中。
线程逃逸:有可能被外部线程访问到,譬如赋值给类变量或可以在其他线程中访问的实例变量。
可以参考:https://blog.csdn.net/yangzl2008/article/details/43202969

4.3.2 TLAB 线程本地分配缓存

TLAB的全称是Thread Local Allocation Buffer,即线程本地分配缓存区,这是一个线程专用的内存分配区域。
  由于对象一般会分配在堆上,而堆是全局共享的。因此在同一时间,可能会有多个线程在堆上申请空间。因此,每次对象分配都必须要进行同步(虚拟机采用CAS配上失败重试的方式保证更新操作的原子性),而在竞争激烈的场合分配的效率又会进一步下降。JVM使用TLAB来避免多线程冲突,在给对象分配内存时,每个线程使用自己的TLAB,这样可以避免线程同步,提高了对象分配的效率。
  TLAB本身占用Eden区空间,在开启TLAB的情况下,虚拟机会为每个Java线程分配一块TLAB空间。参数-XX:+UseTLAB开启TLAB,默认是开启的。TLAB空间的内存非常小,缺省情况下仅占有整个Eden空间的1%,当然可以通过选项-XX:TLABWasteTargetPercent设置TLAB空间所占用Eden空间的百分比大小。
  由于TLAB空间一般不会很大,因此大对象无法在TLAB上进行分配,总是会直接分配在堆上。TLAB空间由于比较小,因此很容易装满。比如,一个100K的空间,已经使用了80KB,当需要再分配一个30KB的对象时,肯定就无能为力了。这时虚拟机会有两种选择,第一,废弃当前TLAB,这样就会浪费20KB空间;第二,将这30KB的对象直接分配在堆上,保留当前的TLAB,这样可以希望将来有小于20KB的对象分配请求可以直接使用这块空间。实际上虚拟机内部会维护一个叫作refill_waste的值,当请求对象大于refill_waste时,会选择在堆中分配,若小于该值,则会废弃当前TLAB,新建TLAB来分配对象。这个阈值可以使用TLABRefillWasteFraction来调整,它表示TLAB中允许产生这种浪费的比例。默认值为64,即表示使用约为1/64的TLAB空间作为refill_waste。默认情况下,TLAB和refill_waste都会在运行时不断调整的,使系统的运行状态达到最优。如果想要禁用自动调整TLAB的大小,可以使用-XX:-ResizeTLAB禁用ResizeTLAB,并使用-XX:TLABSize手工指定一个TLAB的大小。

-XX:+PrintTLAB可以跟踪TLAB的使用情况。一般不建议手工修改TLAB相关参数,推荐使用虚拟机默认行为。


分享:

低价透明

统一报价,无隐形消费

金牌服务

一对一专属顾问7*24小时金牌服务

信息保密

个人信息安全有保障

售后无忧

服务出问题客服经理全程跟进