jvm垃圾回收的时候如何确定是垃圾?是否知道什么是GCROOT,以及什么对象可以作为GCRoot。
如何确定是垃圾:简单说内存中不被使用的空间就是垃圾
1、引用指针法:
是通过引用计数器来进行判断的,如果有地方引用它计数器加一,如果引用失效就减一。这样会产生一个问题就是循环引用的问题,如果对象之间循环引用就不会被回收。
2、GCRoot可达性分析:
通过一系列GCRoot对象作为起点,开始向下搜索如果一个对象到GCRoot没有任何引用链相连的话就称为这个对象不可达,就是垃圾对象。
什么对象可以作为GCRoot对象:
虚拟机栈(栈帧中的本地变量表)中引用的对象。
方法区中的类静态属性引用的对象。
方法区中常量引用的对象。
本地方法栈中native方法引用的对象。
你说过你坐过jvm调优和jvm参数配置,请问如何盘点查看jvm系统默认值。
jvm参数类型:
1、标配参数:
从java语言开始到现在都存在的参数(jdk各版本不会变化)
java -version
java -help
java -showversion
2、X参数:(了解)
java -Xint (解释执行)
java -Xcomp (第一次使用就编译成本地代码)
java -Xmixed (混合模式,先编译后执行)
例如:
3、XX参数:(重点)
Boolean类型:
-XX:+或者-某个属性,+代表开启这个属性,-代表关闭这个属性。
jps -l //查看后台正在运行的java程序
jinfo -flag + 属性名 + 进程编号
jinfo -flag PrintGCDetails 13632 //是否打印GC收集细节。
例如:
kv键值对类型:
MaxTenuringThreshold //经过几次GC才能进入老年代。
例如:
查看某个参数的默认值
jinfo -flag MetaspaceSize + 进程编号 //查看元空间默认值大小。
如何配置:
再次查看:
jps -l //查看进程编号
jinfo -flag MetaspaceSize + 进程编号
如何查看当前运行程序的配置
jinfo -flags +进程编号
题外话
-Xmx //最大堆内存 配置-Xmx1024m
-Xms //初始堆内存 配置-Xms1024m
算X参数还是XX参数?(XX参数)
总结:调参其实就是Boolean参数是否开启,还有就是kv键值对从多大调到多大。
查看jvm初始化参数(重点)
java -XX:+PrintFlagsInitial //zzz
查看修改过的jvm参数
java -XX:+PrintFlagsFinal
主要区分:=和=的区别
:=代表人工修改过的
=代表默认参数值
打印出命令行参数值
java -XX:+PrintCommandLineFlags
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fGp0pYrJ-1601111719351)(http://www.ztinfo.xyz/upload/2020/1/image-95e8c387f832443487f52bb445c60eb3.png)]
你用过jvm常用基本配置参数有哪些
-Xms 初始堆内存大小一般默认为物理内存1/64
-Xmx 最大堆内存大小一般默认为物理内存1/4
-Xss 单个线程初始话栈内存空间一般512k-1024k
如图:
-Xss初始栈内存分析(栈管运行,堆管存储)
什么也没配置在windows环境下查看后台java进程发现初始栈内存为0
在命令行配置-Xss1024k再次运行
查看oracle官方文档
https://docs.oracle.com/javase/8/docs/index.html
在linux(64)环境下默认1024k
windows环境依赖于虚拟内存。
-Xmn 设置堆中的新生代大小。
-XX:MetaspaceSize
设置元空间大小,元空间本质和永久代相似,都是对jvm规范中方法区的实现。不过元空间与永久代最大的区别在于,元空间不存在虚拟机中,而是使用本地内存。因此默认情况下元空间的大小仅受本地内存限制。
-XX:+PrintGCDetails
-XX:SurvivorRatio
设置新生代中Eden和S0/S1空间的比例
默认-XX:SurvivorRatio=8, Eden:S0:S1=8:1:1
假如:-XX:SurvivorRatio=4, Eden:S0:S1=4:1:1
-XX:NewRatio
设置新生代和老年代占比
默认-XX:NewRatio=2新生代占1老年代占2,新生代占整个堆内存1/3
假如:-XX:NewRatio=4,新生代占1老年代占4,新生代占整个堆内存1/5
-XX:MaxTenuringThreshould=15
设置垃圾的最大年龄,默认15次会到老年代。
堆内存图
经典示例演示:
-Xms128m -Xmx4096m -Xss1024k -XX:MetaSpaceSize=512m -XX:+PrintCommentLineFlags -XX:+PrintGCDetails -XX:+UserSerialGC
什么也没配置:
配置之后:
GC详细信息分析
-XX:+PrintGCDetails(打印GC详细信息)
默认没有发生GC垃圾回收控制台打印出如下图:
打印出堆内存的详细信息:
当我们故意配置
-xms10m -xmx10m -XX:+PrintGCDetails
在代码中故意new一个大对象
Byte [] b = new Byte[5010241024];
控制台打印如下图:
GC和FullGC
GC主要在新生代,FullGC主要在老年代
GC图解:
FullGC图解:
强引用,软引用,弱引用,虚引用分别是什么?
架构图:
强引用(Reference)
当内存不足时,jvm开始回收内存,对于强引用的对象,就算出现OOM异常也不会对该对象回收,把一个对象赋值给一个引用变量,这个引用变量就是强引用。我们平时用的最多的就是强引用。
如图:
软引用(SoftReference)
当系统内存够用时,发生GC也不会回收,当系统内存不够用时,发生GC会回收。
1、内存够用演示(发生GC也不会被回收)
2、内存不够用演示(发生GC会回收内存)
设置堆内存大小,堆最大内存大小,打印GC详细信息
-Xms=5m -Xmx=5m -XX:+PrintGCDetails
弱引用(WeakReference)
只要发生GC就会回收对象。
软引用和弱引用的应用场景
假如有一个应用需要读取大量的本地图片,
如果每次读取都从硬盘中读取,会严重影响系统性能。
如果全部读取到内存会报OOM异常内存溢出,会把内存占满。
设计思路:
如果用HashMap来保存图片路径和相应图片对象软引用之间的映射关系,在内存不足时会触发GC来回收这些软引用对象。从而有效的避免OOM问题。
WeakHashMap
当key设置为null,发生GC会把WeakHashMap中的元素进行回收。
例如:
虚引用(PhantomReference)fan in tai mu
如果一个对象仅持有虚引用,就和没有任何引用一样,随时都有可能被回收。不能单独使用必须结合引用队列(ReferenceQueue)来使用,在进行GC回收后虚引用对象会被放到引用队列里边,完成对象最后的操作。和Finalize()方法一样。
常见的OOM异常(jvm异常属于错误)
1、java.lang.StackOverflowError //栈内存溢出
一般错误原因使用了递归算法,方法中调用自己。
2、java.lang.OutOfMemoryError:java heap space //堆内存溢出
一般错误原因是因为创建了大对象。
3、GC overhead limit exceeded //GC回收时间过长,我们都知道GC是个单线程,如果频繁GC会让我们的系统变慢,cpu百分之98的资源都用于GC,回收的内存并不理想,频繁创建对象。
4、Direct buffer memory //直接内存挂了。
出现原因主要是写NIO程序经常用到字节流(byteBuffer)来读取和写入数据,这是一种基于通道(Channel)与缓冲区(Buffer)的IO方式。
byteBuffer它可以使用native函数库直接分配堆外内存,然后通过一个存储在堆里边的DirectByteBuffer对象作为这块内存的引用对象,进行操作,这样就避免了java堆和native堆中来回复制数据。
ByteBuffer.allocate(分配),分配jvm堆内存属于GC管辖范围,需要native堆拷贝数据到jvm堆浪费时间。
ByteBuffer.allocateDirect(直接分配),分配本地内存,不属于GC管辖,不用复制数据。
如果不断的分配本地内存,DirectByteBuffer对象就不会被回收,虽然java堆中内存很足,但是直接内存已经满了,程序直接崩溃。
两者的对比图(自己理解的不知道对不对)
5、unable to create new native thread //不能够再创建新的线程了。
出现原因,你的应用创建了太多的线程,超过系统的承载能力。默认Linux系统不是root用户最多能创建1024个线程。
解决办法:
1、想办法降低自己程序中创建线程的数量。
2、更改系统配置,允许创建最大线程数。
ulimit -u //查看本用户允许创建的最大线程数。
vi /etc/security/limits.d/90-nproc.conf //更改配置如图所示
6、Metaspace //元空间溢出
元空间主要存放jvm加载类的信息、常量池、静态变量、即时编译后的代码。
主要原因是通过反射,不断向元空间加载类信息。
GC垃圾回收算法和GC垃圾回收器分别是什么?
GC垃圾回收算法:
1、引用计数算法
2、复制算法
3、标记算法
4、标记整理清除算法
GC垃圾回收器:
1、串行垃圾回收器(Serial)
只有一个线程进行垃圾回收,并且会阻断用户线程。
2、并行垃圾和回收器(Parallel)
多个线程进行垃圾回收,也会阻断用户线程,适用于科学计算弱交互场景
3、并发标记清除垃圾回收器(CMS)
大多数互联网公司默认的垃圾回收器,垃圾回收线程和用户线程同时执行,适应于对响应时间有要求的场景,会产生内存碎片。
4、G1垃圾回收器(G1)
将堆内存分成不同的区域,并发对其进行垃圾回收。
新生代主要用的垃圾回收器
1、串行垃圾回收器(Serial),自动激活老年代Serial Old
一般用于单个应用程序,单核的服务器。
开启:-XX:+UseSerialGC
2、并行垃圾收集器(ParNew)Parallel new Generation(代)
在新生代用并行收集器,默认老年代会自动激活串行垃圾回收器(Serial Old)
不推荐使用了(jdk8)
开启:-XX:+UserParNewGC
3、并行清除(Parallel Scavenge)
在新生代配置并行清除,新生代,老年代都会启动多个线程来收集。
吞吐量大,吞吐量=(程序运行时间-GC回收时间)/程序运行时间。多线程GC耗时短,吞吐量就大。
开启:-XX:+UseParallelGC
新生代和老年代对应垃圾回收器图解:
CMS并发标记清除垃圾回收器详解(用在老年代)
开启:-XX:UseConcMarkSweepGC 开启之后新生代会默认打开ParNew并行垃圾收集器。
CMS回收器分为4步
1、初始标记(会阻断用户线程)
2、并发标记(与用户线程同时进行)
3、重新标记(会阻断用户线程),第2步有可能产生新的垃圾对象。
4、并发清除(和用户线程一起进行)
优点:并发收集停顿低
缺点:产生内存碎片,并发对cpu的压力也比较大。
CMS垃圾回收器一定要在老年代堆内存用尽之前完成垃圾回收,否则就会导致CMS回收器失败转成Serial old(老年代的串行垃圾回收器),造成停顿时间长。
总结
如何选择垃圾回收器
G1垃圾回收器简介
开启:-XX:+UseG1GC
回顾以前三大收集器的特点:
1、年轻代和老年代都是独立且连续的内存块。
2、年轻代收集使用单一的eden+S0+S1进行复制算法。
3、老年代收集必须扫描整个老年代区域内存。
4、都是以快速执行GC为设计原则。
G1是什么?
G1是一种服务端的垃圾回收器,应用在多处理器和大容量内存环境中。在实现高吞吐量的同时,尽可能满足垃圾收集暂停的时间。
G1主要改变Eden、Survivor和Tenured(老年代)等区域不在是连续的,而是变成一个一个大小一样的region,每个region大小1~32M,一个region有可能属于Eden、Survivor和Tenured内存区域。
G1特性:
1、像CMS收集器一样,能与应用线程同时执行。
2、整理空闲空间更快。
3、可以预测GC的停顿时间。
4、不需要更大的java heap(堆)
G1与CMS相比优势?
1、G1不会产生内存碎片
2、用户可以指定GC的挺顿时间单位毫秒。
G1的原理
最大的好处就是不区分新生代,老年代,整个内存化整为零,避免全内存扫描,只需要按照区域来进行扫描即可。
核心思想是将整个堆内存区域分成大小相同的子区域(Region),在JVM启动时会自动设置这些区域大小,启动时可以通过参数-XX:G1HeapRegionSize=n可指定分区大小(1~32M,且必须是2的幂次方)默认将整个堆内存划分成2048个分区,也就是能够支撑的最大内存32M*2048=65536M(64G)
如图所示:
G1垃圾回收器,虽然把堆内存划分成一个一个小内存,但还是分代收集器(还有Eden、Survivor、Old)。
这些Region一部分包含新生代,新生代的垃圾收集仍然采用暂停所有应用线程的方式,将存活对象拷贝到老年代或者幸存区(Survivor)se vai v。
在拷贝的同时也完成了堆的压缩,内存小,操作快,不会产生内存碎片。
在G1中,还有一种特殊的区域,叫(Humongous)巨型区,在G1中如果一个对象占分区容量的50%以上,G1收集器就会认为这是一个巨型对象,这些巨型对象默认会被直接分配到老年代,但是如果它是一个短期存活的巨型对象,就会对垃圾回收器造成负面影响,为了解决这个问题,G1划分了一个Humongous,他专门来存放巨型对象,G1会寻找连续的H区进行存储,为了能找到连续的H区,有时候要启动Full GC,在老年代中执行。
常用参数设置
-XX:+UseG1GC //使用G1垃圾回收器
-XX:G1HeapRegionSize = n //设置Region区域大小,值是2的幂次方,大小1~32M
-XX:MaxGCPauseMillis=n //最大GC停顿时间,jvm尽可能会小于这个时间(但不保证)
-XX:InitiatingHeapOccupancyPercent=n //堆占用了多少就触发GC默认是45
-XX:ConcGCThreads=n //并发GC使用的线程数
jvmGC配合SpringBoot微服务使用
在本地可以配置SpringBoot入口函数配置Jvm参数。
在Linux服务器上java -jar xxxx.war 启动一个.jsp集中式项目。
如果启动时候想加入jvm参数。
java -server jvm参数 -jar xxx.war
例如:
java -server -Xms1024M -Xmx1024m -XX:+UseG1GC -jar xxx.war