JVM 一些常见问题QA

2024-06-13 11:04
文章标签 java jvm 常见问题 qa

本文主要是介绍JVM 一些常见问题QA,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

GC Roots


  1. 虚拟机栈中引用的对象;

  2. 本地方法栈中JNI引用的对象;

  3. 方法区中类静态变量引用的对象;

  4. 方法区中常量引用的对象;

Full GC是Minor GC+Major GC吗?


Minor GC:回收年轻代;

Major GC:回收老年代,经常会伴随至少一次的Minor GC;

Full GC:回收整个堆,年轻代+老年代;

新生代的S区动态年龄如何计算?


Hotspot遍历所有对象时,按照年龄从小到大对其所占用的大小进行累积,当累积的某个年龄大小超过了survivor区的一半时,取这个年龄和MaxTenuringThreshold中更小的一个值,作为新的晋升年龄阈值。例如:Survivor区 = 64M,desired survivor = 32M,此时Survivor区中age<=2的对象累计大小为41M,41M大于32M,所以晋升年龄阈值被设置为2,下次Minor GC时将年龄超过2的对象被晋升到老年代。

何时发生Minor GC和Full GC?(CMS为例)


  • 何时触发Minor GC?
    • 在系统需要在新生代Eden申请内存空间不足的时候触发,JVM会判断是否要先Major GC(最理想的就是直接复制到S1完事,内部解决,根本用不到Major GC)。

  • 何时触发Major GC?

    • 没开启担保机制,Minor GC前先进行一次Major GC;(1.7及以后默认开启了担保机制,1.6及以前则需要手动配置担保机制)

    • 开启了担保机制,但平均晋升对象大小 > 老年代剩余空间;

    • 设置了-XX:CMSInitiatingOccupancyFaction参数,后台会有一个线程定时扫描,如果老年代使用空间超过比值,也会执行Major GC。

  • 何时触发Full GC?

    • CMS GC时出现promotion failed和concurrent mode failure(concurrent mode failure发生的原因一般是CMS正在进行,但是由于老年代空间不足,需要尽快回收老年代里面的不再被使用的对象,这时停止所有的线程,同时终止CMS,直接进行Serial Old GC);

    • 主动触发Full GC(执行jmap -histo:live [pid])来避免碎片问题。

JVM参数配置


  1. -XX:PretenureSizeThreshold:对象大小超过设置的值,则直接分配在old区,默认为0,全部在eden分配。 此参数只对Serial及ParNew两款收集器有效。

  2. -XX:TargetSurvivorRatio=n:设置Survivor区的目标使用率,即当survivor区GC后使用率超过这个值,就可能会使较小的年龄的对象晋升;

活跃数据的大小是指,应用程序稳定运行时长期存活对象在堆中占用的空间大小,也就是Full GC后堆中老年代占用空间的大小。可以通过GC日志中Full GC之后老年代数据大小得出,比较准确的方法是在程序稳定后,多次获取GC数据,通过取平均值的方式计算活跃数据的大小。活跃数据和各分区之间的比例关系如下:

    

Major GC要不要扫描年轻代?


  1. Serial,Parallel scavenge,Parallel old回收老年代的时候还会回收年轻代,Major GC升级为Full GC;

  2. CMS有两种模式

    1. 设置了-XX:+CMSScavengeBeforeRemark,回收老年代前会先回收年轻代;

    2. 没设置的话,会扫描年轻代,但只回收老年代;

  3. G1比较特殊,它无论处于何种模式下,都不需要扫描别的代,只需要处理一下记忆集;

Safepoint和OopMap


OopMap映射表记录哪些位置存放着对象引用,协助根节点快速完成枚举过程。

Safepoint是一些特定位置,当线程运行到这些位置时,线程中的某些状态是确定的。在safePoint可以记录OopMap信息,线程在safePoint停顿,虚拟机进行GC。 

SafePoint一般出现在以下位置:循环体的结尾、方法返回前、调用方法的call之后、抛出异常的位置。这些位置保证线程不会长时间运行而无法到达safePoint,避免其他线程都停顿等待本线程。

JVM中的VMThread会一直等待直到VMOperationQueue中有操作请求出现,比如GC请求。而VMThread要开始工作必须要等到所有的Java线程进入到safepoint。JVM维护了一个数据结构,记录了所有的线程,所以它可以快速检查所有线程的状态。当有GC请求时,所有进入到safepoint的Java线程会在一个Thread_Lock锁阻塞,直到当JVM操作完成后,VM释放Thread_Lock,阻塞的Java线程才能继续运行。

safepoint只能处理正在运行的线程,它们可以主动运行到safepoint。而一些Sleep或者被blocked的线程则靠safe region来完成。线程进入到safe region的时候先标识自己进入了safe region,等它被唤醒准备离开safe region的时候,先检查能否离开,如果GC已经完成,那么可以离开,否则就在safe region呆着。

String.intern()原理


参考:深入解析String#intern

总结:jdk7 版本对 intern 操作和常量池都做了一定的修改。主要包括2点:

  • 将String常量池 从 Perm 区移动到了 Java Heap区

  • String#intern 方法时,如果存在堆中的对象,会直接保存对象的引用,而不会重新创建对象(jdk1.6会在String常量池创建对象)

看懂这个代码,就算理解了

STW期间,新请求如何处理?

如果是RPC请求,很可能会在socket buffer上阻塞,IO读写暂停,比如刚好一个请求到来,就卡住等STW结束再进服务。

怎么排查线上GC问题 & GC优化


1,首先,在进行GC优化之前,需要确认项目的架构和代码等已经没有优化空间。不能指望一个系统架构或代码有缺陷的应用,通过GC优化来飞跃;

2,其次,可以看出虚拟机内部已有很多优化来保证应用的稳定运行,所以不要为了调优而调优,不当的调优可能适得其反; 

3,最后,GC优化是一个系统而复杂的工作,没有万能的调优策略可以满足所有的性能指标,比如可能不能同时满足低延时和高吞吐;

现象:服务抖动,成功率变低,GC耗时长。

1,看内存对象有无异常,大对象长时间存活;

jmap -histo:live PID

2,看是否cache过多,长时间存活:GC前后内存使用率对比;

3,如果GC要处理内存大也会更耗时,提前GC,提高频率降低每次清理内存;

// Old区使用了50%的时候触发GC
-XX:+UseCMSInitiatingOccupancyOnly
-XX:CMSInitiatingOccupancyFraction=50

4,增加对象在年轻代内存中驻留的时间,降低GC频率;

// 注意动态年龄问题,可能生效的不一定是配置的这个(详见CMS动态年龄)
-XX:MaxTenuringThreshold=31

5,调整年轻代比例;

如何选择各分区大小应该依赖应用程序中对象生命周期的分布情况:

如果应用存在大量的短期对象,应该选择较大的年轻代;如果存在相对较多的持久对象,老年代应该适当增大。

经典比例参考:从实际案例聊聊Java应用的GC优化 - 美团技术团队

6,CMS-Remark之前强制进行年轻代GC;

// 这两个参数强制在remark阶段之前先进行一次年轻代GC,这样需要remark的内存量就不会太多(详见CMS跨带引用)
-XX:+ScavengeBeforeFullGC(Parallel GC的参数,ParNew不用配置)
-XX:+CMSScavengeBeforeRemark

Tips:

1.CMS-remark阶段需要对堆中所有的内存对象进行处理,如果在这个阶段之前强制执行一次年轻代的GC会大量减少remark需要处理的内存数量,进而降低JVM卡顿对成功率的影响;

2.对于Java HTTP服务,JVM的卡顿时间应该小于HTTP客户端的调用超时时间,否则JVM卡顿会对成功率造成影响;

这篇关于JVM 一些常见问题QA的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1057119

相关文章

Java对异常的认识与异常的处理小结

《Java对异常的认识与异常的处理小结》Java程序在运行时可能出现的错误或非正常情况称为异常,下面给大家介绍Java对异常的认识与异常的处理,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参... 目录一、认识异常与异常类型。二、异常的处理三、总结 一、认识异常与异常类型。(1)简单定义-什么是

SpringBoot项目配置logback-spring.xml屏蔽特定路径的日志

《SpringBoot项目配置logback-spring.xml屏蔽特定路径的日志》在SpringBoot项目中,使用logback-spring.xml配置屏蔽特定路径的日志有两种常用方式,文中的... 目录方案一:基础配置(直接关闭目标路径日志)方案二:结合 Spring Profile 按环境屏蔽关

Java使用HttpClient实现图片下载与本地保存功能

《Java使用HttpClient实现图片下载与本地保存功能》在当今数字化时代,网络资源的获取与处理已成为软件开发中的常见需求,其中,图片作为网络上最常见的资源之一,其下载与保存功能在许多应用场景中都... 目录引言一、Apache HttpClient简介二、技术栈与环境准备三、实现图片下载与保存功能1.

SpringBoot排查和解决JSON解析错误(400 Bad Request)的方法

《SpringBoot排查和解决JSON解析错误(400BadRequest)的方法》在开发SpringBootRESTfulAPI时,客户端与服务端的数据交互通常使用JSON格式,然而,JSON... 目录问题背景1. 问题描述2. 错误分析解决方案1. 手动重新输入jsON2. 使用工具清理JSON3.

java中long的一些常见用法

《java中long的一些常见用法》在Java中,long是一种基本数据类型,用于表示长整型数值,接下来通过本文给大家介绍java中long的一些常见用法,感兴趣的朋友一起看看吧... 在Java中,long是一种基本数据类型,用于表示长整型数值。它的取值范围比int更大,从-922337203685477

java Long 与long之间的转换流程

《javaLong与long之间的转换流程》Long类提供了一些方法,用于在long和其他数据类型(如String)之间进行转换,本文将详细介绍如何在Java中实现Long和long之间的转换,感... 目录概述流程步骤1:将long转换为Long对象步骤2:将Longhttp://www.cppcns.c

SpringBoot集成LiteFlow实现轻量级工作流引擎的详细过程

《SpringBoot集成LiteFlow实现轻量级工作流引擎的详细过程》LiteFlow是一款专注于逻辑驱动流程编排的轻量级框架,它以组件化方式快速构建和执行业务流程,有效解耦复杂业务逻辑,下面给大... 目录一、基础概念1.1 组件(Component)1.2 规则(Rule)1.3 上下文(Conte

SpringBoot服务获取Pod当前IP的两种方案

《SpringBoot服务获取Pod当前IP的两种方案》在Kubernetes集群中,SpringBoot服务获取Pod当前IP的方案主要有两种,通过环境变量注入或通过Java代码动态获取网络接口IP... 目录方案一:通过 Kubernetes Downward API 注入环境变量原理步骤方案二:通过

Springboot整合Redis主从实践

《Springboot整合Redis主从实践》:本文主要介绍Springboot整合Redis主从的实例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言原配置现配置测试LettuceConnectionFactory.setShareNativeConnect

Java中Map.Entry()含义及方法使用代码

《Java中Map.Entry()含义及方法使用代码》:本文主要介绍Java中Map.Entry()含义及方法使用的相关资料,Map.Entry是Java中Map的静态内部接口,用于表示键值对,其... 目录前言 Map.Entry作用核心方法常见使用场景1. 遍历 Map 的所有键值对2. 直接修改 Ma