让建站和SEO变得简单

让不懂建站的用户快速建站,让会建站的提高建站效率!

2
栏目分类
热点资讯
最新资讯 你的位置:王牌国际 > 最新资讯 > JVM:我就想清楚我是如何没的
JVM:我就想清楚我是如何没的 发布日期:2022-03-13 20:38    点击次数:202

咱们都清楚 Java 情势都是跑在 JVM 上的,一朝 JVM 有什么风吹草动,势必会影响做事的踏实性。红运的话,做事会发生抖动,可能有部分请求出现延长或稀奇。祸害的话,JVM 径直崩溃,导致做事统统中断。

这可不是什么善事,与 JVM 所有崩溃的,除了做事,还有咱们的心态。

所谓的 JVM 崩溃,一般情况下即是指内存溢出,也即是 OutOfMemoryError 和 StackOverflowError。另外还有一种情况即是堆外内存占用过大,这种情况会导致 JVM 场地机器的内存被撑爆,从而导致机器重启等稀奇情况发生,咱们把这种情况叫做内存透露。

那什么情况下会变成 JVM 崩溃呢,有哪几种类型的崩溃呢?俗语说,自甘堕落,方能屡战屡捷。了解了发生崩溃的原因,才能更好的惩处 JVM 崩溃问题。

 

当先如故放出 JVM 内存模子图,JVM 要聚首起来是很详尽的,借助底下这张图不错具象化的了解 JVM 内存模子,而发生溢出的几个部分都不错在图中找到。在 JDK 8 中,长久代依然不存在了,拔帜树帜的是元空间(metaspace)。

 

 

 

 

底下就以 Hotspot JDK 8 为配景,看一下 JVM 内存溢出和内存透露的几种情况。

当先开荒 JVM 启动参数,完了堆空间大小,堆空间开荒为 20M,其中重生代10M,元空间10M,并指定垃圾集聚算法选择 CMS 算法。之后的例子都会使用这套参数。

-XX:+UseConcMarkSweepGC -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 -XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses -XX:+CMSClassUnloadingEnabled -XX:+ParallelRefProcEnabled -XX:+CMSScavengeBeforeRemark -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+HeapDumpOnOutOfMemoryError -XX:MetaspaceSize=10M -XX:MaxMetaspaceSize=10M -XX:HeapDumpPath=/Users/fengzheng/jvmlog 

堆溢出

堆溢出,应该是最常见的一种内存溢出的场景了。JVM 均分派绝大多数对象实例和数组都存在堆上,另外堆内存亦然垃圾集聚器责任的主要战场。

当咱们的 Java 情势启动的期间,会指定堆空间的大小,新建对象和数组的期间会分派到堆上头,当新对象苦求空间的期间,要是堆内存不够了,就会发生垃圾集聚行为,大多数期间会发生在重生代,叫做 Minor GC。当重生代回收完成,空间仍然不够的话,会发生一次 FullGC。FullGC 后,空间仍然不够,此时就会发生 OOM 失实,也即是堆溢出。

模拟一下这个场景

private final static int _1K = 1024;  public static void main(String[] args){   List<byte[]> byteList = new ArrayList<>();   quietlyWaitingForCrashHeap(byteList); }  public static void quietlyWaitingForCrashHeap(List<byte[]> byteList) {   try {     while (true) {       byteList.add(new byte[500 * _1K]);       //Thread.sleep(1000);       Thread.sleep(100);     }   } catch (InterruptedException e) {    } } 

上头的情势会不竭的向List

底下是情势运行之后的效力,经过垃圾回收最终如故莫得富裕的空间,从而发生 java.lang.OutOfMemoryError: Java heap space稀奇。

image-20201016211017630

 

发生堆内存溢出的根柢原因即是使用中的对象大小进步了堆内存大小。

堆内存空间开荒的太小,要把柄预估的实践使用堆大小合理的开荒堆空间开荒。

情势有症结导致,某些静态变量不竭的增大,举例缓存数据失实的运转念,导致缓存无额外的加多,最终导致堆内存溢出。针对这种情况,只怕没什么好情势,除了做好测试除外,即是在问题发生后做好日记分析。

栈溢出

造谣机栈是用来存储局部变量表、操作数栈、动态贯串、情势出口等信息的,每调用一个 Java 情势就会为此情势在造谣机栈中生成栈帧。

栈除了包括造谣机栈除外,还包括土产货情势栈,当调用的情势是土产货情势(举例 C 言语扫尾的情势)时,会用到土产货情势栈。不外,在 HotSpot 造谣机中,造谣机栈和土产货情势栈被合二为一了。

模拟栈溢出场景

public static void main(String[] args){   stackOverflow(); }  /** * stackoverflow */ public static void stackOverflow() {   stackOverflow(); } 

在上头的代码中,stackOverflow() 情势的调用是一个无尽递归的经过,莫得递归出口。前边说了,每调用一个情势就会在造谣机栈中生成栈帧,无尽的递归,必定变成无尽的生成栈帧,临了导致栈空间被填满,从而发生溢出。

image-20201019122447325

 

上头模拟了最常见的一种情状,产生这种情状的原因很可能是由于情势 bug 导致的,一般来说,递归必定会有递归出口,要是由于某些原因导致了情势在扩张的经过中无法达到出口条目,那就会变成这种稀奇。还有即是轮回体,轮回体的轮回次数要是过大,也有可能出现栈溢出。

另外还可能是其他相比退却易出现的原因,比如创建的线程数过多,线程创建要在造谣机栈均分派空间,要是创建线程过多,可能会出现 OutOfMemoryError稀奇,然而一般来说,都会用线程池的情势代替手动创建线程的样式,是以,这种情况退却易出现。

元空间溢出用于存储已被造谣机加载的类信息,常量,静态变量,即时编译(JIT)后的代码等数据,在 JDK 8 中,依然用 metaSpace 代替了长久代的。默许情况下 metaSpace 的大小是莫得完了的,也即是场地做事器的实践内存大小,然而,一般情况下,最佳如故开荒元空间的大小。

一般在产生大都动态生成类的情景中,可能会出现元空间的内存溢出。

模拟元空间溢出

public static void main(String[] args){   List<byte[]> byteList = new ArrayList<>();   //quietlyWaitingForCrashHeap(byteList);   // stackOverflow();   methodAreaOverflow(); }  public static void methodAreaOverflow() {   int i = 0;   while (true) {     Enhancer enhancer = new Enhancer();     enhancer.setUseCache(false);     enhancer.setSuperclass(MethodOverflow.class);     enhancer.setCallback(new MethodInterceptor() {       @Override       public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {         return methodProxy.invokeSuper(o, objects);       }     });     enhancer.create();     System.out.println(++i);   } } 

通过 CGLIB 的样式动态的创建许多个动态类,这么一来,类信息就会越来越多的存到元空间,从而导致元空间溢出。

image-20201019163227576

 

举例在使用 Spring、 MyBatis 等工夫框架的期间会动态创建 Bean 实例类,另外,Spring AOP 也会产活泼态代理类。

堆外内存溢出

大多数情况下,内存都会在 JVM 堆内存均分派,很少情况下需要径直在堆外分派内存空间。使用堆外内存的几个公道是:

在进度间不错分享,减少造谣机间的复制 对垃圾回收停顿的改善:要是欺诈某些永远存活并大都存在的对象,普通会触发YGC概况FullGC,不错推敲把这些对象放到堆外。过大的堆会影响Java欺诈的性能。要是使用堆外内存的话,堆外内存是直接纳操作系统经管( 而不是造谣机 )。这么做的效力即是能保持一个较小的堆内内存,以减少垃圾集聚对欺诈的影响。 在某些场景下不错提高情势I/O主宰的性能。少去了将数据从堆内内存拷贝到堆外内存的情势。

普通在需要大都频繁的进行 IO 操作的期间会用到堆外内存,举例 Netty、RocketMQ 等使用到了堆外内存,标的即是为了加速速率。

是以,在出现系统内存占用过大的情况时,排查堆栈无果后,不错看一下堆外内存的使用情况,望望是不是堆外内存溢出了。

回顾

预先做好建设

JVM 问题本人即是相比详尽和难以直觉发现的,是以在技俩上线前除了做好代码逻辑的测试外,还要对 JVM 参数进行合理建设,把柄欺诈情势的体量和特色选拔好符合的参数,比如堆栈大小、垃圾集聚器种类等等。

另外,垃圾集聚日记一定要有保留,还有即是发生内存溢出时要保存 dump 文献。

事中做好监控

在情势上线运行的经过中,做好 JVM 的监控责任,比如用 Spring Admin 这种相比轻量的监控用具,概况大型技俩用 Cat、SkyWallking 等这些辞别式链路监控系统。

过后做好现场保护和分析

 

再合理的参数建设和监控平台,也未免不发生稀奇,这亦然很正常的,不出现稀奇才有问题好吧。在发生稀奇之后,要实时的保留现场,要是是多实例欺诈,不错暂时将发生稀奇的实例做下线处理,然后再进行问题的排查。要是是单实例的做事,那要实时的证实最新的日记和dump依然留存好,证实完成后,再选择失实让做事重启。

 本文转载自微信公众号「古时的风筝」,不错通过以下二维码温雅。转载本文请关联古时的风筝公众号。

 



上一篇:没有了

下一篇:蚂蚁取消展示基金销售榜单,意味着啥?