java常用指令:
javac -> 编译 java -> 运行
jps -> 查看当前java相关进程
jinfo ->查看某一参数具体值
jinfo -flags 进程号 打出该java进程下所有JVM配置信息
jinfo -flag PrintGCDetails 进程号 -> 查看某个java进程 是否开启某个参数(PrintGCDetails打印GC详情)
jinfo -flag MetaSpace 进程号-> 查看某个Java进程 元空间大小
java -XX:+PrintCommandLineFlags 查看GC垃圾回收器
JVM堆内存空间分配:
方法区是一种概念,规范,元空间(永久代)是方法区的实现,包括:装载后类的信息,静态变量,常量池,即时编译后的代码
元空间/永久代内 什么情况下会发生垃圾回收?
1. 该类的所有对象都已经在堆内存中被回收
2.该类的类加载器已经被回收
3.该类的class对象都没有任何引用
满足三个条件则该类会在方法区内被回收
基于Hotspot虚拟机的TLAB
一般常说,栈是线程私有的,堆是共享的。 这句话并不完全正确,堆中Eden区会为每个线程预先留一小块内存空间用来创建对象 (称之为TLAB分配,即Thread Local Allocation Buffer)
1. 为什么这么做?
防止多线程同时要去堆内存开辟空间,可能会造成多个线程争抢同一块内存地址并发问题
2. TLAB特点?
每个线程在Eden区开辟的一小块空间,
只是在“分配”这个动作上是线程独享的,至于在读取、垃圾回收等动作上都是线程共享的,
TLAB空间的内存也非常小,默认情况下仅占有整个Eden空间的1%。
3. TLAB带来的问题?
如果一个对象需要的空间大小超过TLAB中剩余的空间大小,则直接在堆内存中对该对象进行内存分配。
如果一个对象需要的空间大小超过TLAB中剩余的空间大小,则废弃当前TLAB,重新申请TLAB空间再次进行内存分配。
总结来说就是TLAB的剩余大小不满足新创建的对象大小,虚拟机定义了一个refill_waste的值,“最大浪费空间”。新创建的对象大于这个值则直接在堆内存分配,否则废弃当前TLAB 重新申请再分配。
System.gc()的理解
1.在默认情况下,通过System. gc()或者Runtime . getRuntime() .gc()的调用,会显式触发Full GC,同时对老年代和新生代进行回收,尝试释放被丢弃对象占用的内存。(System. gc()的底层就是Runtime . getRuntime() .gc() )
2.System. gc()只是提醒JVM的垃圾回收器执行Full GC,但是不确定是否马上执行,还有可能完全被拒绝。System. gc()与Runtime . getRuntime().gc()的作用一样。
四大垃圾回收算法
1.引用计数法
2.复制算法 缺点:浪费空间 优点:不会产生内存碎片
作用于年轻代Eden区 From区和To区 复制-》清空-》交换 (复制之后有交换,谁空谁为To区)Eden区满了触发young区垃圾回收,对Eden和From区进行一次垃圾回收,将两个区剩余的对象复制一份到To区,然后清空,然后To和From交换。如果对象达到老年标准则进入老年代(交换15次)。
3.标记清除算法 先标记再清除 优点:节约内存空间 缺点:产生内存碎片
4.标记整理算法 先标记,再清除,最后整理把多余的碎片整理成整团 在标记清除算法上,不会产生内存碎片,增加了整理的耗时
七大垃圾回收器
串行 并行 并发 G1 ——四种主要垃圾回收器
串行垃圾回收器: 为单线程设计,只使用一个线程进行垃圾回收,执行垃圾回收同时会暂停用户线程
并行垃圾回收器:串行垃圾回收器的加强版,有多个线程进行垃圾回收,执行垃圾回收的同时会暂停用户线程
并发垃圾回收器: 垃圾回收器线程和用户线程交叉执行,能做到尽量小的停顿用户线程(会产生内存碎片)
G1垃圾回收器: 将堆内存分割成不同区域,并发进行垃圾回收
并发标记清除GC——CMS: 用于老年代 (希望最小时间停顿收集的GC回收器 一般用于大型互联网应用垃圾回收)
1 初始标记(标记一下GC Roots能直接关联的对象,也就是静态变量和栈引用的局部变量) 2 并发标记(标记所有GC Root的间接引用对象) 3 再次标记(再标记第二阶段里新创建的对象,是否GC 引用到) 4 并发清除
只在首次标记和再次标记时是完全停顿的,并发标记和并发清除的时间都可与应用线程并发进行。
最耗时阶段在于 并发标记与并发清除阶段,好在两阶段都可以多线程执行 并且不需要STW。
优点:保持较小停顿 缺点: 1 对CPU压力很高 2 产生内存碎片(可以设置一个参数 多少次full GC之后进行一次压缩full GC整理碎片)
老年区串行收集器作为CMS收集失败的后备收集器.
该参数配置为 CMS垃圾回收器多少次Full GC后会执行一次标记整理算法
G1垃圾回收器——G1:目的为了取代jdk8之前的CMS垃圾收集器
1.物理上不区分年轻代老年代,将整个堆内存分成规格大小的豆腐块Region(1-32m大小) ,每个豆腐块属性标识了新生代,Eden,Survivor,养老代等标记,物理上不是连续的,逻辑上连续
Humongous-大对象存储 ,
2.整体采用标记整理算法,局部采用复制算法,不会产生内存碎片
2.GC能与应用程序并发执行,高吞吐量同时尽量减少停顿时间
3.预测GC停顿时间机制,用户可以指定期望停顿时间,GC垃圾收集会尽量小于期望时间(根据停顿时间去收集垃圾最多的豆腐块区域)
优点:没碎片,减少停顿,期望精准停控时间
G1回收过程
小区域内收集+连续内存块
GCRoots
如何判断一个对象是否可被回收: 枚举根节点做可达性分析(根搜索路径)
从GC根开始通过引用关系进行遍历,遍历不到的节点对象视为死亡。
GCRoots -> GC根的对象:1.栈中局部变量引用的对象 2.方法区中类静态属性引用的对象 3.方法区中常量引用的对象 4.本地方法中Native引用的对象
finalize关键字,
用作GCRoot时垂死挣扎的一次机会
发生GC时,会先判断对象是否执行过finalize 方法,如果未执行,则会先执行 finalize 方法,我们可以在此方法里将当前对象与 GC Roots 关联,这样执行 finalize 方法之后,GC 会再次判断对象是否可达,
如果不可达,则会被回收,如果可达,则不回收!
finalize 方法只会被执行一次,如果第一次执行 finalize 方法此对象变成了可达确实不会回收,但如果对象再次被 GC,则会忽略 finalize 方法,对象会被回收!这一点切记!
常见JVM参数:
java -XX:+Print Flags Initial 查看JVM默认初始参数值
java -XX:+Print Flags final 查看JVM修改后的参数值 = :=
JVM参数:
1.标配参数 自打jdk娘胎出来 一直很稳定几乎没变化 如 java -version java -help
2.-X参数 默认 -Xmixed混合模式
3.-XX参数
3.1. Boolean类型参数 · -XX: -PrintGCDetails 关闭打印GC收集细节 -XX:+PrintGCDetails 打开打印GC收集细节
3.2. K-V设值类型参数 -XX: -MetaspaceSize=128m
-Xms 初始化堆内存大小 等价于 -XX:initialHeapSize 默认大小物理内存的1/64
-Xmx 最大化堆内存大小 等价于 -XX:MaxHeapSize 默认物理内存1/4
-Xss 单个线程初始栈空间 等价于ThreadStackSize 默认512k-1024k
其他参数包括: 设置伊甸区,from区和to区比例(默认8:1:1) 设置新生区和养老区比例 (默认1:2) 设置新生区经过多少轮GC进入老年区(默认15次)
引用问题Reference:
Reference 强引用, 永远不会被垃圾GC回收的对象,即使死都不回收
SoftReference 软引用, 内存足够时不会回收,内存不足时会GC回收 应用场景:大量加载图片 每次从本地读取大量图片消耗性能,全部缓存到内存,可能内存不足,由此提出软引用和弱引用。
WeekReference 弱引用, 只要GC就会被回收
PhantomReference 虚引用 用于监控一个对象的回收状态 必须与引用队列联合使用
ReferenceQueue queue = new ReferenceQueue();
Object obj1 = new Object();
PhantomReference phantom = new PhantomRefence(obj,queue);
WeekHashMap key为 弱引用,如果Key被置空,底层Node<key,value>节点就会被回收。
PhantomReference维护一个队列,
PhantomReference的引用级别属于弱引用,并且调用获取对象的方法永远返回null
作用是: 如果PhantomReference引用的对象被回收,PhantomReference会加入到队列中,通过queue.poll()获取,因此可判断出他的对象是不是被回收了,
判断之后我们就可以相应的处理,可参考DirectByteBuffer的Cleaner
OOM篇
StackOverFlowError
OutOfMemoryError: java heap space 栈溢出
OutOfMemoryError: GC overhead limit exceeded GC回收时间过长,用了98%的时间,回收不到2%的内存。由于每次只回收2% 导致内存又快速被占满 导致再次GC
OutOfMemoryError: Direct buffer memory Native本地内存满了。 写NIO程序时常用ByteBuffer来读写数据,一种基于通道Channel,缓冲区Buffer的I/O方式,可以通过Native函数库直接在堆外分配内存给读写数据,然后堆内对象DirectByteBuffer引用这块Native内存。
OutOfMemoryError: unable to create new native Thread 创建了过多的线程,一个进程创建这么多的线程超出了系统承载
OutOfMemoryError: MetaSpace
排查实战;
top 命令 找到cpu占比最高的进程
ps -ef 找到对应的进程号
ps -mp 进程号 -o thread,tid,time 找出进程中具体的线程号 tid
将10进制的线程号转为16进制 (英文字母小写)
jstack 进程号 | grep tid(十六进制的线程号) 查看日志