深入理解 JVM 笔记

内存划分

线程私有:

虚拟机栈|本地方法栈(hotspot不做区分)==》局部变量表|操作数栈|动态链接|返回地址

程序计数器==》由于Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要一个独立的程序计数器,各条线程之间计数器互不影响,独立存储。

线程共享(需要回收):

永久代|方法区==》Class文件中的版本、字段、方法、接口、常量池(符号引用|直接引用)等描述信息、运行时常量池(String.intern() ..){

String a = "dsadsa";
String b = new String("xzzz");
b.intern();

这中间,"dsadsa"会在编译期进入常量池,"xzzz" 会在运行期进入运行时常量池。

》新生代|老年代>对象实例|数组分配内存

直接内存==》目前主要是NIO 分配堆外内存 缓冲区

HOTSPOT运行机制:

对象创建

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

在Java语言里面,类型的加载、连接和初始化过程都是在程序运行期间完成的,这种策略虽然会令类加载时稍微增加一些性能开销,但是会为Java应用程序提供高度的灵活性,Java里天生可以动态扩展的语言性就是依赖运行期动态加载和动态连接这个特点实现的。例如,如果编写一个面向接口的应用程序,可以等到运行时再指定其实际的实现类;

如果内存规整可以使用指针碰撞 如果不规整则使用空闲列表 给对象分配内多个线程共享内存给对象分配坑定会遇到线程安全问题,类似多个线程减库存,,因此方案如下:

  1. 线程同步,这样创建对象的效率大打折扣
  2. TLAB 本地线程分配缓冲:先给每个线程预留一部分内存做分配,不够在同步

对象初始化值 布尔类型为false 引用为null int 为0

对象构造:

object=headder+instance data+padding

对象头(markword +类型指针+数组长度(如果对象是数组的话)) + 对象体+padding(对象体补充到8字节整数倍)

对象回收机制:

可达性算法:GC Roots的对象包括下面几种:

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象。
  • 本地方法栈中JNI(即一般说的Native方法)引用的对象。
  • 方法区中类静态属性引用的对象、常量引用的对象。

注:如果是软|弱|虚引用对象则在GC发生时也可回收

标记清除算法-缺陷:效率问题|空间问题(不连续碎片 也容易触发垃圾回收)

==》复制算法

分成两块内存:分配对象在其中一块儿,标记清除之后复制剩下的对象到另一块儿(缺点:内存利用率太低)

==》分成三块内存:分成Eden区(80%) S0区 S1区(20%)

Eden+S0清理之后剩余对象复制到S1 然后下一次清理Eden+S1(不够分配则进入老年代):SuvivorRate=8 xms10 xmn10 (=Eden区8,S0 S1为1)==>分配 2、2、2、4 则分配4的时候Eden和S1都不够因此触发minor GC,222被移动到老年代,4分配到Eden。

==>G1算法

新生代Minor GC \ 老年代Full GC 时间相差10倍左右

逃逸分析-栈上分配

栈上分配的对象会随着出栈直接释放内存也不用回收处理,但是需要分析对象是不是仅在方法体内部有效(没有引用外部对象)

指令重排序

a=0 a=1 c=a

这段程序cpu执行的时候可能a=1还没线程缓存刷新到主内存,那么c取出来的a的值就还是0。

单线程下有数据依赖性(as if serial)保证,多线程就没法保证了,因为多线程下,在单线程看来可以保证依赖性在多线程下就没法保证了。这个时候只能做同步处理,比如先写后读。

锁的内存语义

synchronized 代码块的时候:

线程1、2(由监视器规则保证happens before)对同一值做加锁写操作

  • 线程1进同步代码块--获取锁- 从主内存中取出value然后+1放入自己的本地内存
  • 线程1出同步代码块--释放锁-将自己本地内存alue刷新到主内存中-同时通知其他线程(同步共享变量到主内存中)
  • 线程2进同步代码块--清除自己缓存的value从主内存中取出value

Volatile内存语义:

可见性保证的原理:

  • 当写入一个volatile变量的时候,Java内存模型会把该线程对应的本地内存中的共享变量值刷新到主内存中。
  • 当读一个volatile变量的时候,Java内存会把当前线程对应的本地内存中的共享变量置为无效然后从主内存中读取共享变量。

final内存语义:

写final域操作禁止重排序到构造方法之外保证了读取final域坑定是已经初始化好了的,实现方式是利用storestore内存屏障保证变量不逃逸。 深入理解 JVM

posted @ 2019-07-07 18:40  dustyhope  阅读(193)  评论(0编辑  收藏  举报