jvm-jvm类加载机制
2022-02-22 17:43:52 1 举报
AI智能生成
jvm-jvm类加载机制
作者其他创作
大纲/内容
jvm剖析
1.类加载机制
① 运行一个Main方法的流程
java com.tuling.jvm.Math
windows系统下java.exe调用底层的jvm.dll文件创建Java虚拟机(C++实现)
创建一个引导类加载器实例(C++实现)
C++调用Java代码创建JVM启动器实例sun.misc.Launcher该类由引导类加载器负责加载创建其它类加载器
sun.misc.Launcher.getLauncher()
获取运行类自己的加载器ClassLoader,是APPClassLoader的实例
launcher.getClassLoader()
调用loadClass加载要运行的类Math
classLoader.loadClass("com.tuling.jvm.Math");
加载完成时候JVM会执行Math类的main方法入口
C++发起调用
Main.main()
java程序运行结束
jvm销毁
② classLoad.loadClass("xxx")解析
加载
在硬盘上查找并通过IO读入字节码文件,使用到类时才会加载,例如调用类的
main()方法,new对象等等,在加载阶段会在内存中生成一个代表这个类的
java.lang.Class对象,作为方法区这个类的各种数据的访问入口
验证
校验字节码文件的正确性
准备
给类的静态变量分配内存,并赋予默认值
解析
将符号引用替换为直接引用,该阶段会把一些静态方法(符号引用,比如
main()方法)替换为指向数据所存内存的指针或句柄等(直接引用),这是所谓的静态链接过
程(类加载期间完成),动态链接是在程序运行期间完成的将符号引用替换为直接引用
初始化
对类的静态变量初始化为指定的值,执行静态代码块
使用
卸载
③ 类加载器
类型
引导类加载器
扩展类加载器
应用程序类加载器
自定义加载器
双亲委派机制
源码
在类AppClassLoader的loadClass方法中实现
调用父类的loadClass
判断类是否是自己加载过的,如果不是,就交给父类加载器去loadClass
都会调用c = findClass(name); 在加载器的类路径中查找并加载该类
设计的目的
沙箱安全机制
避免类的重复加载
打破双亲委派机制
自定义自己的类加载器
重写loadClass方法,不指派给父加载器
实例:Tomcat中的类加载器
SharedClassLoader:每个项目都能使用
WebAppClassLoader:各个应用有自己的类加载器,实现相互隔离
JasperLoader:加载JSP文件编译出来的.class文件
实现热加载
全盘负责委托机制
自定义类加载器
继承java.lang.ClassLoad类
重写findClass方法
2.jvm内存模型(JDK8)
内存结构
本地方法栈
存放native修饰的方法:C语言的实现
栈
线程
栈帧:一个方法对应一个栈帧内存空间
局部变量表
如果变量值指向的时一个对象,那么这个变量存放的就是这个对象在堆里面的地址
操作数栈
操作数在进行操作时,临时存放的一块空间
动态链接
方法出口
调用方法时的位置信息,以便方法结束之后能继续执行以后的代码
对代码进行反汇编
javap -c xxx.class
查看jvm自己的汇编指令
打开Java VisualVM
windows命令行输入:jvisualvm
堆
new出来的对象
结构
老年代
默认占堆的空间比例:2/3
年轻代
Eden区 默认占比8/10
Survivor区
s0 默认占比1/10
s0 默认占比1/10
程序计数器
每个线程独有的,记录代码执行的位置
每执行一行代码,字节码执行引擎会修改程序计数器的值
方法区(元空间)
常量
静态变量
如果静态变量指向对象,那么这个变量存放的就是这个对象在堆里面的地址
javap -v xxx.class
里面有常量池:Constant pool
类信息
方法名
方法返回值
...一切跟Class有关的信息
jvm参数设置
堆
-Xms
-Xmx
新生代:-Xmn
方法区
-XX:Metaspace
-XX:MaxMetaspace
栈
-Xss 每个栈的大小 默认是1m
线程数太多会触发 StackOverflowError
设置格式
‐Xms2048M ‐Xmx2048M ‐Xmn1024M ‐Xss512K ‐XX:MetaspaceSize=256M ‐XX:MaxMetaspaceSize=256M
jvm调优的核心思想:减少GC的次数,主要是Full GC的次数。
案例:日均百万级订单交易系统如何设置JVM参数
假设亿级流量电商(每日点击上亿次),假定每个用户点击20-30次
说明日活用户500万,付费转化率10%
日均50万订单,正常在3-4小时产生,每秒几十单
大促时,集中在前几分钟产生,每秒1000多单
假设订单系统有3台:4核8G。每一台承接300多单
假设每个对象1kb,每秒300kb对象产生
考虑其他对象(库存,优惠券,积分等),放大20倍,每秒产生300*20kb对象
同时还有其他操作,比如订单查询,再放大10倍,每秒产生300*20*10kb=60m对象
这60M对象1秒后都变成垃圾对象
如果设置参数-Xms3072M -Xmx3072M ‐Xss1M ‐XX:MetaspaceSize=256M ‐XX:MaxMetaspaceSize=256M
此时老年代2G,Eden800m,S0:100m,S1:100m
Eden区满了之后,发生minor gc,会有60m对象进入survivor区
此时会触发对象动态年龄机制,此时60m会进入老年代
所以初步设置为:‐Xms3072M ‐Xmx3072M ‐Xmn2048M ‐Xss1M ‐XX:MetaspaceSize=256M ‐XX:MaxMetaspaceSize=256M
3.JVM对象创建和内存分配机制
对象创建流程
类加载检查
如果没有加载,会加载类
分配内存
划分内存方法
指针碰撞(默认)
这里的指针是:分配内存和未分配内存的临界点标记
针对内存是规整的
空闲列表
针对内存是不规整的
jvm会委会一张空闲内存的表
解决并发问题方法(对象内存争抢问题)
CAS
本地线程分配缓冲(TLAB)(默认)
开启:-XX:+UseTLAB
关闭:-XX:-UseTLAB
指定TLAB大小:-XX:TLABSize
如果放不下,会走CAS方式
初始化
将分配到的内存空间初始化为零值
可以在TLAB分配时进行
设置对象头 32位占4字节,64位占8字节
存储对象自身的运行时数据
HashCode
GC分代年龄
锁状态标志
线程持有的锁
偏向线程
偏向时间
Klass Point类型指针
指向类元数据的指针
开启压缩 占4字节
没有开启压缩 占8字节
执行<init> 方法
属性赋值
执行构造方法
查看对象大小:jol-core包
对象指针压缩
64位操作系统支持指针压缩
启用:XX:+UseCompressedOops(默认开启)
禁止指针压缩:XX:UseCompressedOops
设计目的
在64位平台的HotSpot中使用32位指针,内存使用会多出1.5倍左右,使用较大指针在主内存和缓存之间移动数据,
占用较大宽带,同时GC也会承受较大压力
为了减少64位平台下内存的消耗,启用指针压缩功能
在jvm中,32位地址最大支持4G内存(2的32次方),可以通过对对象指针的压缩编码、解码方式进行优化,使得jvm
只用32位地址就可以支持更大的内存配置(小于等于32G)
堆内存小于4G时,不需要启用指针压缩,jvm会直接去除高32位地址,即使用低虚拟地址空间
堆内存大于32G时,压缩指针会失效,会强制使用64位(即8字节)来对java对象寻址,这就会出现1的问题,所以堆内
存不要大于32G为好
对象的内存分配
栈上分配
逃逸分析
分析对象动态作用域
开启参数: -XX:+DoEscapeAnalysis
JDK7 之后默认开启
关闭参数: -XX:-DoEscapeAnalysis
标量替换
开启参数:-XX:+EliminateAllocations
JDK7 之后默认开启
标量和聚合量
不会被外部访问,随方法结束而销毁
对象不能太大,不然一个栈帧放不下
Eden区分配
Minor GC和Full GC
Minor GC/Young GC:指发生新生代的的垃圾收集动作,Minor GC非常频繁,回收速度一般也比较快。
Major GC/Full GC:一般会回收老年代 ,年轻代,方法区的垃圾,Major GC的速度一般会比Minor GC的慢10倍以上。
开启打印GC参数:-XX:+PrintGC -XX:+PrintGCDetail
Eden与Survivor区默认8:1:1
让8:1:1自动变化
-XX:+UseAdaptiveSizePolicy(默认开启)
不让8:1:1自动变化
-XX:-UseAdaptiveSizePolicy
大对象直接进入老年代
-XX:PretenureSizeThreshold设置大对象的大小
只在 Serial 和ParNew两个收集器下
有效。
-XX:PretenureSizeThreshold=1000000 (单位是字节) -XX:+UseSerialGC
长期存活的对象进入老年代
默认分代年龄15会进入老年代
CMS收集器默认6岁
参数 -XX:MaxTenuringThreshold
对象动态年龄判断
放对象的S区,有一批对象的总大小大于S区的50%
这个50%可以通过参数设置
-XX:TargetSurvivorRatio
大于等于这批对象年龄最大值的对象会直接进入老年代
判断时机
每一次minor gc之后触发
设计目的
希望可能是长期存活的对象,提前进入老年代
老年代空间分配机制
对象内存回收
引用计数法
无法解决对象直接的相互引用问题
可达性分析算法
GC Roots根节点
线程栈的本地变量
静态变量
本地方法栈的变量
常用引用类型
强引用
软引用
弱引用
虚引用
finalize()方法
对象第一次被标记为垃圾对象时,如果覆盖了finaliz()方法,暂时不会被回收,反之,立即回收
第二次标记,会执行finalize()方法
只会执行一次
无用的类:方法区主要回收的类
判断标准(需同时满足)
该类所有的实例都已经被回收,也就是 Java 堆中不存在该类的任何实例。
加载该类的 ClassLoader 已经被回收。
该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
4.JVM字节码深度分析 // TODO
5.垃圾收集
垃圾收集算法
分代收集理论
复制算法(年轻代)
内存分为两块,每次只用一块
标记整理算法(老年代)
标记清除算法(老年代)
1. 效率问题 (如果需要标记的对象太多,效率不高)
2. 空间问题(标记清除后会产生大量不连续的碎片)
垃圾收集器
按照年代区分
年轻代
Serial
ParNew
Parallel
G1
老年代
CMS
Serial Old
Parallel Old
在JDK1.5以及以前的版本中与Parallel Scavenge收集器搭配使用
另一种用途是作为CMS收集器的后备方案
G1
Serial收集器
开启参数: -XX:+UseSerialGC -XX:+UseSerialOldGC
新生代采用复制算法,老年代采用标记-整理算法。
单线程
Parallel Scavenge收集器
开启参数:-XX:+UseParallelGC(年轻代),-XX:+UseParallelOldGC(老年代)
Serial收集器的多线程版本
新生代采用复制算法,老年代采用标记-整理算法。
JDK8 默认的收集器
吞吐量高
ParNew收集器
开启参数:-XX:+UseParNewGC
新生代采用复制算法,老年代采用标记-整理算法。
跟Parallel收集器很类似
区别主要在于它可以和CMS收集器配合使用
CMS收集器
开启参数: -XX:+UseConcMarkSweepGC(old)
过程
初始标记
暂停所有的其他线程(STW),并记录下gc roots直接能引用的对象,速度很快。
Stop the world
并发标记
和用户线程一起运行
重新标记
使用三色标记的增量更新算法
Stop the world
并发清理
并发重置
重置本次GC过程中的标记数据。
优点
并发收集
低停顿
缺点
对CPU资源敏感(会和服务抢资源);
无法处理浮动垃圾(在并发标记和并发清理阶段又产生垃圾,这种浮动垃圾只能等到下一次gc再清理了);
它使用的回收算法-“标记-清除”算法会导致收集结束时会有大量空间碎片产生,当然通过参数-XX:+UseCMSCompactAtFullCollection可以让jvm在执行完标记清除后再做整理
执行过程中的不确定性,会存在上一次垃圾回收还没执行完,然后垃圾回收又被触发的情况,特别是在并发标记和并发清理阶段会出现,一边回收,系统一边运行,也许没回收完就再次触发full gc,也就是"concurrentmode failure",此时会进入stop the world,用serial old垃圾收集器来回收
核心参数
1. -XX:+UseConcMarkSweepGC:启用cms
2. -XX:ConcGCThreads:并发的GC线程数
3. -XX:+UseCMSCompactAtFullCollection:FullGC之后做压缩整理(减少碎片)
4. -XX:CMSFullGCsBeforeCompaction:多少次FullGC之后压缩一次,默认是0,代表每次FullGC后都会压缩一次
5. -XX:CMSInitiatingOccupancyFraction: 当老年代使用达到该比例时会触发FullGC(默认是92,这是百分比)
6. -XX:+UseCMSInitiatingOccupancyOnly:只使用设定的回收阈值(-XX:CMSInitiatingOccupancyFraction设定的值),如果不指定,JVM仅在第一次使用设定值,后续则会自动调整
7. -XX:+CMSScavengeBeforeRemark:在CMS GC前启动一次minor gc,目的在于减少老年代对年轻代的引用,降低CMS GC的标记阶段时的开销,一般CMS的GC耗时 80%都在标记阶段
8. -XX:+CMSParallellnitialMarkEnabled:表示在初始标记的时候多线程执行,缩短STW
9. -XX:+CMSParallelRemarkEnabled:在重新标记的时候多线程执行,缩短STW;
亿级流量电商系统如何优化JVM参数设置(ParNew+CMS)
‐Xms3072M ‐Xmx3072M ‐Xmn2048M ‐Xss1M ‐XX:MetaspaceSize=256M ‐XX:MaxMetaspaceSize=256M ‐XX:SurvivorRatio=8
2 ‐XX:MaxTenuringThreshold=5 ‐XX:PretenureSizeThreshold=1M ‐XX:+UseParNewGC ‐XX:+UseConcMarkSweepGC
3 ‐XX:CMSInitiatingOccupancyFraction=92 ‐XX:+UseCMSCompactAtFullCollection ‐XX:CMSFullGCsBeforeCompaction=0
G1收集器(Garbage-First)
开启参数:-XX:+UseG1GC
JDK 1.9默认使用 G1
安全点
安全区域
Region
G1将Java堆分成大小相等的独立区域,叫region
region大小等于堆大小除以2048,可以用-XX:G1HeapRegionSize手动指定
默认一开始年轻代对对内存占比是5%,可以用-XX:G1NewSizePercent调整
之后会不断给年轻代增加更多的Region,上限是60%,可以用-XX:G1MaxNewSizePercent调整上限
Eden和survivor区配比保持8:1:1不变
Region可能之前是年轻代,进行垃圾回收之后,可能会变成老年代
Humongous区
专门分配大对象,超过Region50%的叫大对象
一个大对象可能会横跨多个Region区
Full GC会回收年轻代,老年代和Humongous区的垃圾
采用复制算法
收集过程
初始标记
暂停所有其他线程,记录下gc roots直接能引用的对象,速度很快
并发标记
同CMS并发标记
最终标记
同CMS的重新标记
STW
筛选回收(STW)
对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿STW时间(可以用JVM参数-XX:MaxGCPauseMillis指定)来制定回收计划
G1收集器在后台维护了一个优先队列,每次根据所允许的收集时间,优先选择回收价值最大的Region
G1垃圾收集分类
YoungGC
MixedGC
Full GC
参数设置
-XX:+UseG1GC
使用G1收集器
-XX:ParallelGCThreads
指定GC工作的线程数量
-XX:G1HeapRegionSize
指定分区大小
-XX:MaxGCPauseMillis
目标暂停时间(默认200ms)
-XX:G1NewSizePercent
新生代内存初始空间(默认5%,值配置整数,默认就是百分比)
-XX:G1MaxNewSizePercent
新生代内存最大空间
使用场景
50%以上的堆被存活对象占用
对象分配和晋升的速度变化非常大
垃圾回收时间特别长,超过1s
8GB以上的堆内存
停顿时间是500ms以内
ZGC收集器(JDK11加入)
实验性质,几百G内存 使用
三色标记
黑色
灰色
白色
认识几个概念
多标-浮动垃圾
漏标-读写屏障
增量更新
黑色对象一旦新插入了指向
白色对象的引用之后, 它就变回灰色对象了。
原始快照STAB (Snapshot At The Beginning)
对漏标的处理方案
CMS:写屏障 + 增量更新
G1,Shenandoah:写屏障 + SATB
ZGC:读屏障
写屏障
写屏障实现SATB
可以用于记录跨代/区引用的变化
写屏障实现增量更新
读屏障
可以用于支持移动对象的并
发执行等
记忆集与卡表
卡表的维护
6.调优实战
启动一个web应用程序,用jps查看其进程,比如进程为15300
Jmap
jmap -histo 15300 > ./log.txt
num 序号
instances:实例数量
bytes:占用空间大小
class name:类名称
查看堆信息:jmap -heap 15300
堆内存dump
jmap -dump:format=b,format=dumpTest 15300
设置内存溢出自动导出(可能会导不出来)
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=./ (路径)
可以用jvisualvm命令工具导入该dump文件分析
点击文件,装入,选择dump文件
jstack 15300
查找死锁
可以查看线程状态
Thread-1
线程名
prio
线程优先级
tid
线程id
nid
线程对应的本地线程标识nid
java.lang.Thread.State: BLOCKED
线程状态
还可以用jvisualvm自动检测死锁
找出占用cpu最高的线程堆栈信息
使用命令top -p <pid> ,显示你的java进程的内存情况,pid是你的java进程号,比如19663
按H,获取每个线程的内存情况
找到内存和cpu占用最高的线程tid,比如19664
转为十六进制得到 0x4cd0,此为线程id的十六进制表示
执行 jstack 19663|grep -A 10 4cd0,得到线程堆栈信息中 4cd0 这个线程所在行的后面10行,从堆栈中可以发现导致cpu飙高的调
用方法
查看对应的堆栈信息找出可能存在问题的代码
jvisualvm
自动查找死锁
远程连接
普通jar程序JMX端口设置
java ‐Dcom.sun.management.jmxremote.port=8888 ‐Djava.rmi.server.hostname=192.168.50.60 ‐Dcom.sun.management.jmxremot
e.ssl=false ‐Dcom.sun.management.jmxremote.authenticate=false ‐jar microservice‐eureka‐server.jar
-Dcom.sun.management.jmxremote.port 为远程机器的JMX端口
-Djava.rmi.server.hostname 为远程机器IP
tomcat的JMX配置
在catalina.sh文件里的最后一个JAVA_OPTS的赋值语句下一行增加如下配置行
JAVA_OPTS="$JAVA_OPTS ‐Dcom.sun.management.jmxremote.port=8888 ‐Djava.rmi.server.hostname=192.168.50.60 ‐Dcom.sun.ma
nagement.jmxremote.ssl=false ‐Dcom.sun.management.jmxremote.authenticate=false"
Jinfo
查看jvm的参数
jinfo -flags 15300
查看java系统参数
jinfo -sysprops 15300
Jstat
垃圾回收统计
jstat -gc pid
S0C:第一个幸存区的大小,单位KB
S1C:第二个幸存区的大小
S0U:第一个幸存区的使用大小
S1U:第二个幸存区的使用大小
EC:伊甸园区的大小
EU:伊甸园区的使用大小
OC:老年代大小
OU:老年代使用大小
MC:方法区大小(元空间)
MU:方法区使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间,单位s
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间,单位s
GCT:垃圾回收消耗总时间,单位s
堆内存统计
jstat -gccapacity pid
NGCMN:新生代最小容量
NGCMX:新生代最大容量
NGC:当前新生代容量
S0C:第一个幸存区大小
S1C:第二个幸存区的大小
EC:伊甸园区的大小
OGCMN:老年代最小容量
OGCMX:老年代最大容量
OGC:当前老年代大小
OC:当前老年代大小
MCMN:最小元数据容量
MCMX:最大元数据容量
MC:当前元数据空间大小
CCSMN:最小压缩类空间大小
CCSMX:最大压缩类空间大小
CCSC:当前压缩类空间大小
YGC:年轻代gc次数
FGC:老年代GC次数
新生代垃圾回收统计
jstat -gcnew pid
S0C:第一个幸存区的大小
S1C:第二个幸存区的大小
S0U:第一个幸存区的使用大小
S1U:第二个幸存区的使用大小
TT:对象在新生代存活的次数
MTT:对象在新生代存活的最大次数
DSS:期望的幸存区大小
EC:伊甸园区的大小
EU:伊甸园区的使用大小
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间
新生代内存统计
jstat -gcnewcapacity pid
NGCMN:新生代最小容量
NGCMX:新生代最大容量
NGC:当前新生代容量
S0CMX:最大幸存1区大小
S0C:当前幸存1区大小
S1CMX:最大幸存2区大小
S1C:当前幸存2区大小
ECMX:最大伊甸园区大小
EC:当前伊甸园区大小
YGC:年轻代垃圾回收次数
FGC:老年代回收次数
老年代垃圾回收统计
jstat -gcold pid
MC:方法区大小
MU:方法区使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
OC:老年代大小
OU:老年代使用大小
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间
老年代内存统计
jstat -gcoldcapacity pid
OGCMN:老年代最小容量
OGCMX:老年代最大容量
OGC:当前老年代大小
OC:老年代大小
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间
元数据空间统计
jstat -gcmetacapacity pid
MCMN:最小元数据容量
MCMX:最大元数据容量
MC:当前元数据空间大小
CCSMN:最小压缩类空间大小
CCSMX:最大压缩类空间大小
CCSC:当前压缩类空间大小
YGC:年轻代垃圾回收次数
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间
JVM运行情况预估
年轻代对象增长的速率
Young GC的触发频率和每次耗时
每次Young GC后有多少对象存活和进入老年代
Full GC的触发频率和每次耗时
内存泄露到底是怎么回事
7.Arthas详解
官方文档
https://alibaba.github.io/arthas
使用
wget https://alibaba.github.io/arthas/arthas‐boot.jar
用java -jar运行即可,可以识别机器上所有Java进程
选择进程序号1,进入进程信息操作
输入dashboard可以查看整个进程的运行情况,线程、内存、GC、运行环境信息:
输入thread可以查看线程详细情况
输入 thread加上线程ID 可以查看线程堆栈
输入 thread -b 可以查看线程死锁
输入 jad加类的全名 可以反编译,这样可以方便我们查看线上代码是否是正确的版本
使用 ognl 命令可以查看线上系统变量的值,甚至可以修改变量的值
8.GC日志详解
打印GC日志方法,在JVM参数里增加参数, %t 代表时间
‐Xloggc:./gc‐%t.log ‐XX:+PrintGCDetails ‐XX:+PrintGCDateStamps ‐XX:+PrintGCTimeStamps ‐XX:+PrintGCCause
‐XX:+UseGCLogFileRotation ‐XX:NumberOfGCLogFiles=10 ‐XX:GCLogFileSize=100M
Tomcat则直接加在 JAVA_OPTS变量里。
CMS 打印GC日志
‐Xloggc:d:/gc‐cms‐%t.log ‐Xms50M ‐Xmx50M ‐XX:MetaspaceSize=256M ‐XX:MaxMetaspaceSize=256M ‐XX:+PrintGCDetails ‐XX:+P
rintGCDateStamps
‐XX:+PrintGCTimeStamps ‐XX:+PrintGCCause ‐XX:+UseGCLogFileRotation ‐XX:NumberOfGCLogFiles=10 ‐XX:GCLogFileSize=100M
‐XX:+UseParNewGC ‐XX:+UseConcMarkSweepGC
G1打印GC日志
‐Xloggc:d:/gc‐g1‐%t.log ‐Xms50M ‐Xmx50M ‐XX:MetaspaceSize=256M ‐XX:MaxMetaspaceSize=256M ‐XX:+PrintGCDetails ‐XX:+Pr
intGCDateStamps
‐XX:+PrintGCTimeStamps ‐XX:+PrintGCCause ‐XX:+UseGCLogFileRotation ‐XX:NumberOfGCLogFiles=10 ‐XX:GCLogFileSize=100M
‐XX:+UseG1GC
gceasy( https://gceasy.io )
分析日志的网站
9.Class常量池与运行时常量池
javap -v Math.class
生成可读的JVM字节码指令文件
Constant pool 就是class常量池信息
字面量
字面量就是指由字母、数字等构成的字符串或者数值常量
符号引用
类和接口的全限定名
字段的名称和描述符
方法的名称和描述符
字符串常量池
设计思想
字符串的分配,和其他的对象分配一样,耗费高昂的时间与空间代价,作为最基础的数据类型,大量频繁的创建
字符串,极大程度地影响程序的性能
JVM为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化
为字符串开辟一个字符串常量池,类似于缓存区
创建字符串常量时,首先查询字符串常量池是否存在该字符串
存在该字符串,返回引用实例,不存在,实例化该字符串并放入池中
字符串操作(JDK1.7及以上)
直接赋值字符串
String s = "zhuge"; // s指向常量池中的引用
new String();
String s1 = new String("zhuge"); // s1指向内存中的对象引用
intern方法
字符串常量池位置
Jdk1.6及之前
有永久代, 运行时常量池在永久代,运行时常量池包含字符串常量池
Jdk1.7
有永久代,但已经逐步“去永久代”,字符串常量池从永久代里的运行时常量池分离到堆里
Jdk1.8及之后
无永久代,运行时常量池在元空间,字符串常量池里依然在堆里
主要区分字符创 在字符串常量池 还是作为对象分配在Heap上
八种基本类型的包装类和对象池
对象池概念 类似于 字符串常量池概念
整数大于127时 不参考对象池概念
浮点数类型没有实现对象池技术
Double
Float
0 条评论
下一页