jvm知识

jvm笔记

一、合并写问题

cpu中有一块用于合并写的四个字节的buff,当这四个字节存满了要写到内存的数据后,一并更新。这样就大大提高了写的性能

二、乱序性

定义

为了提高cpu的执行效率,比如一条指令需要去内存进行读取数据,这个速度相对于cpu的处理速度来说很慢,所以此时cpu会扫描之后的指令,如果其他指令和现在要的数据没有直接的依赖关系,那么会执行其他的指令。

硬件级别解决乱序性

cpu有几条原语来解决乱序性

sfence(store fence)在这个标志之前的写操作必须在当前指令后的写操作执行之前执行

lfence(load fence)在这个标志之前的读操作必须在当前指令后的读操作之前执行

mfence(mix fence)在这个标志之前的读写操作必须在当前指令后的读写操作之前执行

lock 原子指令。

jvm解决乱序的 规范

LoadLoad屏障

​ 对于load1 loadload load2

​ 在load2以及后续读取操作的执行前,保证load1的读取数据读取完毕。

StoreStore屏障

​ 对于Store1 StoreStore Store2

​ 在store2以及后续写入操作执行前,保证store1的写入数据操作对其他处理器可见。

除此之外还有loadStore storeLoad 都是差不多这个意思!!!!

volatile jvm实现细节

写操作

StoreStoreBarrier

volatile写操作

StoreLoadBarrier

读操作

LoadLoadBarrier

volatile读操作

LoadStoreBarrier

synchronized 解决乱序性

jvm 级别

标识变量时在字节码上增加ACC_SYNCHRONIZED

同步代码块时 通过 monitorenter 和 monitorexit

对象的创建过程

class loading (调用classLoader)–》双亲委派

class linking

  • 验证 判断是否是一个标准的class文件格式
  • 准备 将静态变量赋值默认值 比如int = 0
  • 解析 将引用变为直接引用

class initializing 将静态变量赋初始值 并执行静态代码块

申请对象内存

调用构造方法

  • 成员变量按顺序赋默认值
  • 执行构造方法 1.调用父类super的构造方法(可能是Object类的)2.成员变量赋值初始值(可能,看在构造方法中写没写赋值代码)

对象在内存中的存储布局(上述创建过程的第四步)

普通对象

1.对象头 存储markword 8字节

2.ClassPoint指针,执行当前对象的类

3.实例数据 包括成员变量等

4.Padding对齐 因为cpu的读取是按块读取,那么假设说当前这个对象没对齐之前占用的是15个字节,那么为了保证读取整个块的元素,或加入一个字节进行对齐 加快cpu的读取效率 一定是8的倍数!!!

数组对象

1.对象头 存储markword 8字节

2.ClassPoint指针,执行当前对象的类

3.数组长度

4.数组数据

5.Padding对齐 同上

对象头中具体包括什么

不同状态下markword中存储的信息不同

无锁时 hashcode值 偏向锁标志位 锁标志位 分代年龄位

偏向锁时 线程ID(偏向的线程)epoch 分代年龄 偏向锁标志位 锁标志位

轻量级锁时 指向栈中锁记录的指针 (轻量级锁时在jvm的栈上实现的)锁标志位

重量级锁时 指向互斥锁(重量级锁)的指针 锁标志位

GC标志 当需要回收时 前30位为空 锁标志位

对象怎么定位

比如

T t = new T();

这个t对象是怎么找到T对象的

1.句柄池

通过一个句柄池连接 句柄池中存有两个指针 一个连接的是T这个对象 一个连接的是T的class文件 所以t通过连接这个句柄池可以找到T对象

缺点:有了一个中间步骤 效率没有第二个快

优点:当GC采用三色标记(CMS)算法时,回收效率较高。

2.直接指针

直接连接T对象 T对象连接T的class文件

优点:直接连接 效率高

对象怎么分配(待写 需要学到GC)

java Run-time data areas (JAVA运行时数据区)

  • Programe Counter (PC)程序计数区 每一个线程都有自己的PC 里面存放的是该线程要执行的指令
    • 作用:当该线程执行一部分之后 时间片用完 ,cpu执行其他线程完毕后,再回来执行时,可以通过pc继续执行。
  • Jvm stacks jvm栈 ,每一个线程都有自己的jvm栈,存储的是栈帧
  • native method stacks 每一个线程都有自己的,存放该线程用到的native方法,即c/c++方法,无法调优和操作,不必展开学习。
  • method area ,所有线程共享,存放class结构和数据 不同版本有不同的实现。
    • 1.8之前 叫做 Perm Space (永久区) 字符串常量位于Perm Space。FGC不会清理
    • 1.8和1.8之后 改为 Meta Space (元数据区)字符串常量位于堆中,会触发FGC清理。
  • run-time constantpool 运行时常量池 ,存放常量池数据。
  • heap 堆 ,后面学习垃圾回收GC的时候重点!!!

总结:每一个线程都有自己的PC,jvm栈,native method stacks。method area和heap所有线程共享

栈帧Frame 每一个方法都有自己的栈帧

  • Local variable Table(局部变量表)
  • Operand Stack 操作数栈
  • Dynamic Linking 这个东西其实就是当我的当前方法需要另一个方法时,他会去常量池中找到那个方法形成符号引用。
  • return address 执行完返回的地址(下一个栈帧的地址或者要返回去的栈帧的地址)

三、垃圾

目的:熟悉GC常用算法,熟悉常见的垃圾收集器,具有实际的JVM调优实战经验

发现垃圾算法:
  • 1.reference count RC 引用计数:只要有一个引用指向该对象,该对象存储引用计数的位置就会加一。但这个位置为0时就是一个垃圾,缺点:不能解决循环引用 都是一,但是没有外部引用这一个循环的垃圾,无法解决。
  • 2.Root Searching 根可达算法:从根对象找,只有没找到的都称之为垃圾。
    • 根对象:
      • 1.线程栈变量:这里主要指的是主线程中的线程栈,里面会存储main线程的各种栈帧。
      • 2.静态变量:当一个class load到内存之后会对其中的静态变量进行初始化,这些静态变量引用到的对象也称之为根对象。
      • 3.常量池:如果当前class 需要用到其他 class中的对象,也称之为根对象。
      • 4.jni指针:当你的方法中用到了c或者c++的内些本地方法,这些方法中用到的内些类或者对象也称之为根对象。
垃圾清除算法:
  • 标记清除法
    • 定义:循环扫描两次,第一次查出不可被回收的并进行标记,第二次查出可回收的并进行清理。
    • 优点:算法相对简单,存活对象比较多情况下效率较高。
    • 缺点:两遍扫描,效率偏低;容易产生碎片。
  • Copying 拷贝算法
    • 定义:将内存一分为二,将有用的拷贝到下面,然后再将上面的直接删除。
    • 优点:效率高,没有碎片。适用于存活对象比较少的情况,只扫描一次。
    • 缺点:需要复制和移动对象,需要更改引用。容易造成空间浪费。
  • Mark-Compact 标记与压缩算法。
    • 定义:扫描两次,第一次查出有用的,第二次要将这些有用的向前移动,然后将后面的这些垃圾删掉。
    • 优点:不会产生碎片,方便对象的分配(因为剩余的都是连续的空间),不会产生内存减半。
    • 缺点:扫描两次,需要移动对象,效率偏低。

对象的垃圾回收流程

先判断是否可以在栈上分配空间,因为在栈上分配空间的话,那么当这个对象不需要的时候直接出栈即可,不需要垃圾回收的介入。

如果栈上空间不能分配了, 那么就判断是否是一个大的对象,如果是一个大的对象直接进入老年区,如果不是,那么判断能否tlab(分配到伊甸区的那1%的对象空间中,只属于当前线程,效率高),不论是否tlab都是进入伊甸区,只是tlab效率会高一些,然后进行GC,如果可以被清除,则改对象直接结束,如果不能被清除,则进入s1(survivor1),等到再次GC的时候,判断是否是垃圾,不是则判断年龄是否已经够大了,够大则进入老年区,不够则进入s2。进入老年区的对象,当老年区内存满时会进行full gc 清除垃圾。

除此之外还有一个动态的年龄判断,当进入到s2的对象超过了s2空间的一半时,内些稍大年龄的对象也会放入到老年区。

常见的垃圾回收器:

  • 新生代: serial(最早的,随着jdk诞生就有了,单线程) , parnew, parallel Scavenge(多线程
  • 老年代: serial old, cms, parallel old,
  • 以上都是逻辑分代!!!!
  • 物理分代:G1
  • 不分代:ZGC, shenandosh
  • **debug用:**epsilon
常见组合
  • serial – serial old
  • parallel – parallel old
  • parnew cms
工作原理
  • **serial:**当serial要执行的时候,其他线程必须全部停止(safe point:就是每个线程停止时必须停止在一个安全的位置/点,不会对线程待会执行造成影响。)
    • 缺点:单线程,其他等待线程需要停顿的时间特别长。
  • **parallel scavenge (PS):**jvm默认垃圾回收器。和serial一样,当他工作时其他线程需要停止,但是他是多线程的,而serial是单线程。
  • parNew:parallel scavenge的变种,为了方便和cms一起使用
CMS!!!!

CMS是在jdk1.4后期被引入,是GC的一个里程碑,它开启了并发回收的过程,但是CMS毛病较多,目前没有任何一个版本的jdk默认是CMS

为什么要入并发回收?

​ 无法忍受STW(stop the world),即执行回收线程时,所有其他线程必须停止的这个时间。

从线程角度分析CMS的工作流程。
  • 初始标记(STW,由于开始时的根数据并不多,所以时间很短):cms在程序刚启动时对根数据进行标记。
  • 并发标记(最耗时):可以理解为程序一边执行,另一边产生的垃圾就会被标记。
  • 重新标记(STW,只需要标记少部分新产生的垃圾,时间较短):可能会有新产生的垃圾,所以需要重新标记一下。
  • 并发清理:程序继续执行,CMS线程清理垃圾;但是由于此时程序还在继续,在CMS此时执行清理期间还会产生垃圾,这种垃圾我们称之为浮动垃圾(浮动垃圾需要等待下一次CMS清除)。
CMS的问题
  • 内存碎片化:因为cms用的是标记清除算法,这种算法本身缺点就是会造成内存碎片,而当内存中有很多内存碎片导致新生成的对象没有足够的内存空间分配时,他会让serial old进行清除。而serial old 单线程,stw。
  • 浮动垃圾过多,导致新生成对象没有足够的空间分配,还是请出serial old ,问题同上。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值