JVM

1 篇文章 0 订阅

JVM的内存模型

PC寄存器

     记录当前线程执行的字节码的行号,是线程私有的。

虚拟机栈

     存放的是栈帧,线程私有。
     栈帧包括了局部变量表、操作数栈、动态链接、返回值地址。

本地方法栈

     与虚拟机栈相似,它服务的是本地方法,线程私有。

     对象的实例、数组都存在于堆。GC回收的地方

方法区

     存放已经被加载的类信息、常量、静态变量、编译后的代码。回收的目标是常量池。

GC(垃圾回收)

堆的分类

新生代与老年代

     新生代和老年代的内存比例为1:2,新生代分为三个部分,eden、survivor1和suivivor2。三者的比例是8:1:1,当new一个对象时,如果对象的非常大,就放到老年代,否则放入eden,eden中的对象在经历一次GC后,如果还有引用指向它,那么放入survivor1,在经历一次放入suivivor2,往复,直到一定次数后,如果还存在,则放入老年代。

如何判断对象可以被回收了

  • 引用计数法
         当指向一个对象的引用为0时,就判断这个对象可以被GC清楚,在一个对象被new出来的时候,在对象头中会有一个引用计数器,当一个引用指向这个对象时+1,引用消失的时候-1.
  • 根搜索(正向可达)
         引用计数法无法解决循环引用的问题,根搜索从一个程序启动时会有一些根对象,从这些根对象出发,寻找哪些对象可找到,能找到的对象就不是垃圾,找不到的就可以使用GC。根对象:

垃圾回收算法

Mark-Sweep(标记清除)

     根据跟搜索方法,在堆中标记可回收的对象。然后进行一次扫除,将可回收的对象清除。但是容易形成内存碎片化的现象。

Copying(拷贝)

     将内存分为两块,一半放数据,一般不放,先经过一次标记所有可用的对象,将他们拷贝到另一半的内存上,然后将原来的内存整体擦除。内存浪费

Mark-compact(标记-压缩)

     先标记对象,将不可回收的对象放到已经回收了的对象的位置。无内存浪费。

垃圾收集器

Serial

     单线程的收集器,收集垃圾时必须stop the world,在新生代采用的是复制算法,Serial Old在老年代使用标记整理-算法。

Parallel

     Parallel Scavenge在新生代采用并行的多线程收集,使用复制算法。Parallel Old 在老年代使用多线程收集,采用标记-整理算法。

CMS

G1

    采用分区策略,将堆内存分为多个区。

面试题

解释一下对象的创建过程(Object o = new Object())?(半初始化)

    当执行完上述语句,jvm会在堆空间(假设是在堆空间创建的对象)申请一片内存空间来存放类的实例变量,并为这些实例变量赋予默认值,接着调用该类的构造方法,最后将引用与堆空间中的对象建立连接。

DCL和volatile的问题?(指令重排)

     DCL是指双重检查机制的单例模式,在DCL中,如果没有使用volatile关键来禁止指令重排序,那么在第二个线程获取单例对象时,可能会获得半初始化状态的对象,造成错误。

对象在内存中的存储布局(64位虚拟机)

    普通对象在内存中有四个部分:markword,类型指针,实例数据和对齐。

  • markword:包括Hashcode,gc分代年龄和锁信息。一共8个字节。
  • 类型指针:指向该对象的类元数据,用来确定该对象属于哪个类的实例。一共8个字节(开启指针压缩后位4个字节)。
  • 实例数据:存放对象的实例数据。
  • 对齐:如果上述内容所占的内存大小不是8的倍数,那么会补齐到8的倍数。
  • 数组长度:如果对象为数组,那么还会有四位的数组长度。

锁升级:当对一个对象上锁,首先上的是偏向锁,然后编程轻量级锁(也称自旋锁\无锁\free lock),最后变为重量级锁。

  • 偏向锁:当第一个线程访问时,会获得偏向锁,代表无其他线程抢夺该锁,该线程可以在没有锁竞争的情况下,拿到代码的执行权行。
  • 自旋锁:如果当有其他线程对锁进行抢夺,那么持有偏向锁的线程会被挂起。然后通过CAS进行锁的争夺。每进行一次CAS就是自旋一次。通常对于锁竞争不激烈的情况,且占用锁的时间非常短的代码,自旋锁可以大幅度的提高性能,因为线程在自旋阶段,线程没有被挂起,减少了内核态到用户态切换的开销。但是一旦锁竞争十分激烈,且锁使用时间比较长,就会造成大量的线程处于等待,CPU压力就会上升
  • 重量级锁:当并发访问的线程增多时,JVM会自动将锁升级为重量级锁(synchronized)。

CAS(compare and swap)

     当一个线程需要对共享的内存资源进行修改时,先把原来的数据从内存空间读取出来,当修改完后,再去比较原来的内存空间的值是否和原来的相同,如果相同,那么说明在这个期间没有别的线程操作过这个内存空间的值,直接将原来的值和修改后的值交换。如果原来的内存空间的值发生了改变,那么再次读取修改以后的值,重新进行修改,重复上述步骤。

对象定位

     句柄 和 直接指针

对象分配

     优先在栈上分配(需要满足逃逸判断——该对象只在当前方法中被引用和标量替换——在栈上只存成员变量
     如果对象不满足上述条件,则直接在堆上分配,在堆上分配时,如果对象很大,直接进入老年代。
     如果对象不大,那么直接进入TLAB(线程本地分配缓冲区),如果对象比较大,那么进入Eden区

一个Object占多少个字节

     markword占8个字节,类型指针占8个字节,启动指针压缩后变为四个字节,但是需要补齐到8的倍数,所以为16个字节。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值