jvm虚拟机
2021-03-29 22:28:07 8 举报
AI智能生成
jvm虚拟机知识图谱
作者其他创作
大纲/内容
组成
程序计数器
解释:当前代码执行到了哪一行
内存中
特点:各线程之间独立
虚拟机栈
线程私有,生命周期和线程同生共死
-Xss 执行方法时创建栈帧
局部变量表
操作数栈
动态链接
方法出口
本地方法栈
native方法
不会创建栈帧,直接动态链接到函数库调用
元空间<br>1.8移动到虚拟机之外<br>元空间,只受限物理内存大小
jdk1.7及以前
永久区
XX:PermSize
XX:MaxPermSize
jdk1.8及以后
元空间
XX:MetaspaceSize
XX:MaxMetaspaceSize
运行时常量池<br>1.6在方法区<br>1.7在堆中<br>
常量:"123","hello"
static final
堆
内存分配(new,反射等)<br>
内存分配策略
对象优先在Eden分配
大对象直接进入老年代
-XX:PretenureSizeThreshold,默认0,大于才此值,直接在老年代分配
长期存活的对象将进入老年代
-XX:MaxTenuringThreshold,默认15,对象年龄
每一次yong gc仍然存活,则年龄+1
动态对象年龄判定
年龄1的占用了33%,年龄2的占用了33%,累加和超过默认的TargetSurvivorRatio(50%),年龄2和年龄3的对象都要晋升
空间分配担保
survival空间不够,只要老年代的连续空间大于新生代的对象总大小,<br>或历次晋升的平均大小,进行MiniorGC,否则FullGC
内存区域模型
堆
几乎所有的对象都是在堆上分配,例外:栈上分配
栈
栈上分配<br>跟着函数自动销毁,提高性能<br>
-XX:+UseTLAB:ThreadLocalAllocationBuffer,事先在堆里面为每个线程分配一块私有内存,默认开启<br>
-XX:-EliminateAllocations 标量替换,默认开启<br>
逃逸分析后的对象拆解为多个标量,跟随栈销毁而回收
-XX:+DoEscapeAnalysis 逃逸分析,默认开启
对象的实例作用域是否只在一个方法内
-XX:+PrintGCDetails<br>-XX:+PrintGCDateStamps<br>
本地内存(直接内存)
网络通讯NIO
MaxDirectMemorySize<br>
回收
垃圾收集器
判断回收的对象
可达性分析
GC ROOTS
方法区:类静态属性(引用的对象)<br>
方法区:常量引用的对象 static final<br>
虚拟机栈(局部变量表)中引用的对象,回收栈中的对象<br>
本地方法栈中JNI(即一般说的Native方法)引用的对象
引用计数
优点:快、方便、简单
缺点,循环引用
新生代
Serial
单线程,复制算法,简单高效
ParNew
并行多线程,Serial升级版搭配CMS回收器的首选
Parallel Scavenge
吞吐量优先收集器,并行多线程,本身是server多CPU机器的默认GC方式
主要适合后台运算不需要太多交互的任务
老年代
Serial Old
标记整理算法,单线程
Parallel Old
标记整理算法,并行多线程
CMS 关注最短停顿时间STW<br>Concurrent Mark Sweep
并行与并发收集器
标记清除算法
竟可能缩短垃圾收集时用户线程停止时间
缺点
内存碎片
CPU资源更多
浮动垃圾问题,需要更大的堆空间
G1
跨新生代和老年代
标记整理+化整为零
并行与并发收集器,目标为取代CMS
分区回收,不牺牲吞吐量的前提下完成低停顿的内存回收,可预测停顿
参数
-XX:+UseSerialGC<br>
Serial/Serial Old
-XX:+UseParNewGC<br>
ParNew/Serial Old
和上面基本没区别,一个多线程,一个单线程
-XX:+UseParallelGC
Parallel Scavenge/Serial Old
默认
-XX:+UseParallerOldGC
Paraller Scavenge/Parallel Old
-XX:MaxGCPauseMills<br>
控制最大停顿时间
-XX:GCTimeRatio<br>
允许GC时间占总时间,吞吐量的倒数整数=19,允许GC时间 1/(1+19)=5%
-XX:+UseAdaptiveSizePolicy
一般情况使用它,自适应
-XX:UseConcMarkSweepGC
老年代用CMS,新生代用ParNew
因为内存碎片无法分配内存时会启动Serial Old
-XX:+UseCMSCompactAtFullCollection 默认开启
需要进行FullGC的时候开启内存碎片的整理,无法并发
-XX:CMSFullGCsBeforeCompaction 默认为0,设置多少次不压缩的FullGC后进行一次压缩
-XX:+UseG1GC
维护一个内存表
不同分代,用不同的算法
标记-清除算法
缺点,内存碎片
标记-整理算法
存活对象进行复制整理
缺点:拷贝复制,花费时间
新生代
复制算法
对半分,进行整理
缺点:浪费空间,拷贝复制,花费时间
参数
Xms堆的最小值
Xmx堆的最大值
新生代
Xmn新生代的大小
XX:NewSize新生代最小值
XX:MaxNewSize新生代最大值
对象
存储在堆上
过程
检查加载
检查类定义有没有加载
分配内存<br>
指针碰撞和空闲列表
并发安全问题
CAS失败重试
本地线程分配缓冲
内存空间初始化
对象字段设置默认值,0.null,false
设置
对象头内容设置,gc分代年龄,元数据信息,哈希码等
对象初始化<br>
初始化的代码
访问定位
句柄访问
直接指针<br>hotspot使用的方式
内存布局
对象头<br>
对象自身的运行时数据
类型指针
实例数据
程序代码中所定义的各种类型的字段内容<br>
对齐填充
引用
强引用
new Object
对象的一般状态
软引用
SoftReference
OOM之前会回收掉
对象缓存
弱引用
WeakReference
存活到下次GC前
对象缓存,二级缓存
虚引用
PhantomReference
跟踪对象的垃圾回收
存活到下次GC前
异常情况
OOM<br>内存溢出
堆内存溢出
linkList不断重复放元素
新生代溢出
永久代溢出
Java heap space
String[] s=new String[1000000000]
XX:MaxMetaspaceSize=3m
线程栈溢出
StackOverflow<br>方法递归调用
直接内存溢出
OutOfMemoryError: Direct buffer memory
-XX:MaxDirectMemorySize=10M
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(14 * 1024 * 1024)
内存泄漏
数组元素使用完未赋值为null,导致指针依然存在,无法回收
jdk工具
jps
-小写L
main方法名
-q
进程号
-v
输出jvm参数
-m
输出传入 main 方法的参数
jstat
-gc +进程id+间隔时间+打印次数
-gc
-gccause
-gcnew
-gcold
例:jstat -gc 21159 5s 100
jinfo
jmap
-heap 内存空间状态
jstack
jconsole
VisualVM
javap
-verbose+class文件
查看字节码文件定义内容
java执行子系统
class文件
魔数和class版本<br>
常量池
访问标志
类索引、父类索引、接口索引集合
字段表集合
方法表集合
属性表集合
类加载机制<br>
加载
通过一个类的全限定名来获取定义此类的二进制字节流
将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
验证
文件格式验证
元数据验证
字节码验证
符号引用验证
准备
为类变量分配内存并设置类变量初始值
解析
将常量池内的符号引用替换为直接引用的过程
初始化
遇到new、getstatic、putstatic或invokestatic这4条字节码指令<br>(被final修饰、已在编译期把结果放入常量池的静态字段除外)<br>
使用java.lang.reflect包的方法对类进行反射调用
当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化
虚拟机启动先初始化包含main()方法的那个类
动态语言支持
使用<br>
卸载
方法调用
解析
类的静态方法,构造方法,私有方法<br>
静态分派
方法的重载
参数不同,根据参数的静态类型来判断调用哪个方法
Human h1= new Man();<br>Human是静态类型,Man是动态类型
动态分派<br>运行时才知道调用哪个<br>ctrl跳转只能到父类的方法上
方法的重写,多态
维护虚方法表
根据实例的定义去虚方法表中寻找对应的方法<br>未重写,调用父类的,重写,调用子类的
高效优雅
对象
构造器参数太多,使用Builder
不要实例化的类应该构造器私有
不要创建不必要的对象,包装类
避免使用终结方法
使类和成员的可可访问性最小化
使可变性最小化,final
复合优先于继承
接口优于抽象类
方法
可变参数谨慎使用
返回0长度的数组或集合,不要返回Null
优先使用标准的异常
IllegalArgumentException
参数不合适
IllegalStateException
接收对象状态不对
NullPointException
UnsupportedOperationException
方法必须被重写,aqs中就用到了
通用
枚举代替Int常量
枚举内可以有抽象方法,每种枚举都得实现该方法
外层枚举(构造参数是策略枚举)使用策略枚举(含抽象方法),<br>避免部分枚举代码相同
局部变量作用域最小化
减少GC
减少栈帧的大小
精确计算,避免使用float、double
字符串连接性能
控制方法的大小
性能优化
原则
避免过早优化
进行系统性能测试
寻找系统瓶颈,逐步优化
前端
减少请求数
客户端缓冲
启用压缩
资源文件加载顺序
减少cookie传输
cdn加速
反向代理缓存
web组件分离
缓存
优先考虑
频繁修改的数据,尽量不要缓存,读写比2:1以上
缓存一定要是热点数据
容忍一段时间数据不一致问题
缓存可用性
缓存预热
缓存击穿
布隆过滤器
不存在的数据也缓存起来
集群
异步
程序
代码级别
合适的数据结构
算法
优化
并发编程
资源复用
单例
资源池
jvm<br>
jit编译器
热点编译的概念
选择编译器类型<br>
-server
-client
-XX:+TieredCompilation
1.8默认开启
代码缓存相关-XX:ReservedCodeCacheSize=N
1.7 32-48M
1.8 240M
编译阈值
编译线程
方法内联
简单方法优化,set,get
减少栈帧
方法字节码小于325个字节
-XX:MaxFreqInlinesSize=N
方法小于35个字节,一定会内联
-XX:MaxInlineSize
逃逸分析
GC
目的
GC时间够小
GC次数够少
原则
大部分java应用不需要GC调优
大部分需要GC调优的不是参数问题,是代码问题
GC调优是最后的手段
1.选择合适的GC回收器
2.选择合适的堆大小
3.年轻代在堆中的比量
步骤
1.监控GC状态
2.分析结果,判断是否需要优化
minorGC时间<50ms 10s一次
fullGC时间<1s,频率10分钟以上
参数
-XX:+PrintGCDetails
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeadDumpPath
-Xlogger:logpath
自由主题
0 条评论
下一页