JVM常识

JVM是java文件通过javac后编译,再经过classloader将编译后的文件以及java类库加载进内存再通过字节码解释器,即时编译器(热代码会直接编译)执行引擎交给操作系统。JVM是跨语言的平台,有100多种语言可以直接运行在JVM上,JVM是一种规范,虚拟出来的计算机,class文件包括版本号,常量池等信息。

类的初始化(加载),分为三步loading、linking、initializing(静态变量赋值为初始值),其中linking包括verification、preparation(静态变量赋默认值)、resolution等过程,类加载器有bootstrap加载核心类(底层c++实现、空值)extension加载扩展jar包,app加载classpath的内容,另外还可以自己定义类加载器,采用双亲委派机制,自底向上检查是否加载,如果加载了返回,若没有则再自上向下委派加载(因为安全)父加载器不是加载器的加载器如app的父加载器是ext,app的加载器是null(C++实现的加载)

自定义类加载器就继承classloader,然后重写findclass方法,java是懒加载

tomcat打破了双亲委派机制,每个web app可以加载同名的class(热启动、热部署)

小结:1.class load 静态代码块-默认值-初始值 2.new -申请内存 -默认值 -初始值

硬件层数据一致性问题:cacheline为基本单位,目前64bytes,为了避免伪共享,使用缓存行对齐可以提高执行效率

long类型的赋值不是原子性的,因为32操作系统每次读32位

WCBuffer,合并写(空间很少,4个字节内能合并)

volatile实现细节:可见性:缓存一致性协议

​ 字节码层面:ACC_VOLATILE

​ JVM层面:对valitile修饰的变量前后加两种屏障

​ OS和硬件层面:windows的lock指令

synchronized实现细节:

​ 字节码层面:monitorenter mornitorexit

​ JVM层面:C C++调用操作系统提供的同步机制

​ OS和硬件层面:X86 lock comxchg XXX

对象创建过程:

1、class loading、linking、initializing 2、申请对象内存 3、成员变量赋默认值 4、调用构造方法,成员变量顺序赋初始值,执行构造方法语句

对象在内存中的存储布局:

普通对象:1.对象头 markword 8字节 2.classpointer 指针 3.实例数据 4.对齐(对象整个为8的倍数)

数组对象:1.对象头 2.classpointer 3.数组长度 4.数组数据 5.对齐

对象头里有什么:锁标志和GC标志 GC为什么最大15:4位

对象定位:句柄池,指向一个空间,空间指向class和对象(GC效率高)直接指针:直接指向对象(取效率高,hotspot)

对象怎么分配:栈能放下就在栈,不能,若对象大 则分配到堆,不大则分配到线程本地区(占用eden,默认1%) new object大小16字节

指令集:基于栈的指令集(JVM)基于寄存器的指令集(汇编语言)

Runtime Data Area

PC(程序计数器) 存放指令位置

JVM Stack 存储栈帧 Frame:

​ 1.Local Variable Table 局部变量表

​ 2.Operand Stack 操作栈(对long的处理,多数虚拟机都是原子的,不用加volatile)

​ 3.Dynamic Linking 指向Runtime Constant Pool,有就直接用,没有就动态解析

​ 4.return address

Heap

Method Area 存储类结构

​ 1.Perm Space(<1.8)

​ 字符串常量位于PermSpace FGC不会清理 大小启动的时候指定,不能变

​ 2.Meta Space(>=1.8)

​ 字符串常量位于堆 会触发FGC清理 不设定的话,最大就是物理内存

Runtime Constant Pool

Native Method Stack

Direct Memory:JVM可以直接访问内核空间的内存(OS管理的内存)NIO,提高效率,实现zero copy

每个线程都有自己独立的PC、VMS、NMS,所有线程共享Heap、Method Area Meta Space

常用指令

init 构造方法 clinit静态方法块(没显示调用)

Store load

invoke

​ 1.InvokStatic

​ 2.InvokeVirtual

​ 3.InvokeInterface

​ 4.InvokeSpecial 可以直接定位,不需要多态的方法: private方法,构造方法

​ 5.InvokeDynamic :JVM最难的指令 lambda表达式或者反射或者其他动态语言scala kotlin,或者CGLib ASM动态产生class会用到的指令

GC

如何定位垃圾:引用计数算法,根可达算法

常见GC算法:Mark-Sweep(标记清除)(适用于对象多,效率低)、Copying(拷贝)(适用于对象少)、Mark-Compact(标记压缩,效率低)

JVM内存分代模型(用于分代垃圾回收算法)

1.部分垃圾回收器使用的模型

​ 除Epsilon ZGC Shenandon之外的GC都是使用逻辑分代模型

​ G1逻辑分代,物理不分代,其他双分代

2.新生代+老年代+永久代(1.7)/元数据区(1.8)

​ 1.永久代 元数据-class

​ 2.永久代必须指定大小限制,元数据可以设置,也可以不设置无上限(受于物理内存)

​ 3.字符串常量 1.7 - 永久代,1.8堆

​ 4.MethodArea逻辑概念 -永久代/元数据区

3.新生代 = Eden + 2个suvivor区

​ YGC回收之后,大多数的对象会被回收,活着的进入s0,再回收,活着的进入s1,超过15次就进入老年代 年龄1和年龄2累加总和超过50%进入老年代 1.尽量减少FGC 2. MinorGC = YGC 3.MajorGC = FG

​ YGC期间survivor区空间不够了 空间担保直接进入老年代

垃圾回收器:STW(stop the world) safe point

第一种组合:Serial和Serial old 单cpu效率最高 单线程 在老年代采用Mark-Sweep、Mark-Compact

第二种组合:Parrallel Scavenge Parrallel old采用Copying,多线程

第三种组合:ParNew(时间厉害):Parrallel Scavenge(吞吐量厉害)的增强和CMS配合使用

CMS:concurrent(并行) mark sweep 无法忍受STW 老年代的 过程:初始标记-STW-并发标记-STW-重新标记-并发处理

CMS的问题:

​ 1.内存碎片化 2.浮动垃圾 发生问题后请Serial old来清理 解决方法:降低CMS触发阈值(让FGC的阈值降低,流出空间给浮动垃圾)

其他垃圾回收机制:G1、ZGC、Shenandon、Eplison 采用三色算法(自身和指向都标记过的黑色,自身标记过的黑色,都没标记的白色,问题:漏表,解决:CMS用关注引用增量,黑色重新标记为灰色 G1采用SATB 关注引用的删除,当引用消失,把这个引用推到GC的堆栈,保证还能被扫描到,避免重复扫描)RSet(其他Region中的对象到本Region的引用)

吞吐量:用户代码时间/(用户代码执行时间+垃圾回收时间)

响应时间:STW越短,响应时间越好

所谓调优就是最求吞吐量还是响应时间。看应用场景,科学计算数据挖掘就是吞吐量优先(PS+PO),网站就是响应时优先(G1)

JVM调优(重启…)

​ 1.根据需求进行JVM规划和预调优

​ 2.优化运行JVM运行环境

​ 3.解决JVM运行过程中出现的各种问题(OOM)

从规划开始

  • 一切调优,从业务场景出发

  • 无监控,不调优(压力测试,能看结果)

  • 步骤

    选择垃圾回收策略

    计算内存需求(经验值1.5G)

    选择CPU(越高越好)

    设置日志参数

    观察日志情况

​ HotSpot参数分类

​ 标准:- 开头,所有的HotSpot都支持

​ 非标准:-X 开头,特定版本HotSpot支持

​ 不稳定:-XX 下一个版本可能就没了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值