JVM原理和调优(读这一篇就够了)

前言

抛2个问题:

1、export JAVA_OPTS=“-Xms256m -Xmx512m -Xss256m -XX:PermSize=512m -XX:MaxPermSize=1024m -Djava.rmi.server.hostname=136.64.45.24 -Dcom.sun.management.jmxremote.port=9315 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false”

—上面的"-Xms256m -Xmx512m -Xss256m -XX:PermSize=512m -XX:MaxPermSize=1024m“分别是什么意思

2、为什么调整上面这几个参数就能让java程序不卡顿,或者不报错oom

3、java.lang.OutOfMemoryError: Java heap space和java.lang.StackOverflowError是否熟悉,分别表示了什么错误,背后出现了什么问题

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------

一、JVM内存模型

  • 堆:所有new对象都放在堆里,这也是JVM优化的重点。

  • 栈:也叫线程栈,是每个线程独立分配的内存空间,存储的包括局部变量,操作数,动态链接,方法出口。它和堆的关系其实是堆的指针引用。

  • 本地方法栈:为虚拟机用到的Native方法服务。Native方法是java通过JNI直接调用本地C/C++库,可以认为是暴露给java的本地接口。

  • 方法区(元空间):类装载系统会把class信息放到方法区,通过字节码引擎执行。会放常量(运行时常量池,那么什么是静态常量池),静态变量,类元信息。jdk8以后就叫元空间。

  • 程序计数器:记录执行的代码行数的行号,比如线程暂停回来后,继续执行,就需要读取行号,继续向下执行

(一)栈的核心组成部分

  • 局部变量表: 用于存放方法中的局部变量和参数

  • 操作数栈: 用于存放方法执行过程中产生的中间结果

  • 动态链接:方法区里面的符号引用转为直接引用(即:给出地址)

  • 方法出口:记录当前执行方法执行完毕后返回主方法的下一行位置

(二)堆

所有的new对象最开始都放到Eden(伊甸)区,当Eden区沾满之后,JVM会进行一次minor gc,将发现依然存活的对象放到Survivor区,同时讲分代年龄+1(存储在objectheader里面),会清理eden和form区域,通过复制等算法再次将存活对象放到to区域,依次类推来回复制,当分代年龄达到15时,会挪到老年代,比如静态变量,数据库连接池,缓存就属于“老不死”。当老年代内存被占满时,就会触发fullgc,fullgc会对整个堆进行垃圾回收,因此非常耗时,时间很长,我们要尽量降低fullgc的频次。

题外话:minorgc如何进行垃圾回收呢,这里面运用到一个可达性分析算法,里面有一个GCroots根的概念很关键,要理解一下,gc会根据该算法找出该对象的整条GCroot链,定义为有效存活对象,会移动到survivor区域;剩下的对象由于找不到对象引用,因此定义为无效对象,从而进行回收。具体如下:

(为什么年轻代使用复制算法,老年代使用标记-清除算法?)

还有一种情况,只要伊甸区移动的内存大于存活区的1/2时,会直接放到老年代。

当old区内存满了后,就会触发fullgc,但是发现引用还在(list还是运行),因此没法回收,但是死循环又一直在new对象,old区无内存可用,于是就OOM。

在fullgc的时候针对整个heap进行寻找垃圾对象,因此会停顿用户线程(STW)。为什么要停止呢,如果不停止的话,意味着回收线程和用户线程同步执行,回收线程会认为用户线程的内存不是垃圾对象,因此不予理会,但如果用户线程很快执行完,它使用的对象就变成垃圾对象了,这时候回收线程不知道该如何处理,因此为了更好的控制内存,fullgc的时候,必须暂停用户线程,回收完成后,再恢复用户线程。因此问题就出现了,如果频繁的fullgc,而且收集时间非常长(因为针对整个堆内存回收)就会频繁的停止用户线程,导致系统功能暂时不可用,也就是卡顿现象或无响应。因此优化的点就一目了然:减少fullgc次数,让年轻代去收集释放垃圾对象。

二、JVM调优

为什么调优其实上面已经提前说了,所以我们调优的目的是什么呢?就是让系统更加的丝滑,让用户体验变得更好。

以下是一个亿级流量网站的一个并发计算过程demo图:

以下是关键数据计算:

  • 日活:1E/20 = 500W。要估算每个人一天的日点击数。

  • 日均订单:50W。这里要找业务确定转化率。

  • 大促并发数:50W/50s = 1000单左右。这里要估算并发时间,这里拍脑袋是1分钟内。

  • 单机并发数:1000/n。n表示是机器节点。

  • 每单请求的数据量:1kb * 1000/n。1kb是单个请求的data大小,这个研发可以根据请求datajson算出来。

  • 单机的内存使用:300kb * 20 * 10 = 60M。300kb是n=3,20是下单逻辑中会有加减库存,优惠券,积分系统接口,10是订单查询等其他外部操作。

初始化jvm配置如下:

对应的jvm内存模型是:

  1. 每一秒产生60M对象,均放到eden区

  2. 到第13秒时,eden区内存即将占满(60*13 =780M),触发minorgc

  3. 这时候会对前12秒的对象进行回收,第13秒产生的对象由于还在执行,因此判断为有效对象,会被移动到S0区

  4. 这时候会触发jvm另外一个机制–动态年龄判断,即发现该对象大小大于survivor区内存大小的50%,会直接进入old区。因此该60M对象会直接移动到old区。因此会出现每过13秒,会有60M数据进入old区

  5. 所以old区大概多久会被占满,2000M/60 *13 = 433s。也就是大概433s后就会触发一次fullgc

  6. 因此问题来了,如果这个大促持续数个小时,而每隔7分钟就执行一次fullgc,势必影响用户体验和性能。那么怎么优化呢

解决策略:我们完全可以把old区的这60M对象让它在survivor区就回收,减少old区的fullgc次数。因此我们需要对jvm参数进行优化配置。(阿里面试题:能否对JVM调优,使其几乎不发生fullgc。答案是能)

将年轻代的内存调大(-Xmn2048M)

所以,jvm优化后的内存分配模型就出来了:

2个好处:

  • 原来13秒就minorgc,现在延长到25秒

  • 60M对象会在survivor区就会被回收,不会跑到old区触发fullgc

(全文完)

(喜欢视频的可以去读下这个:https://www.bilibili.com/video/av79368741/?p=5

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
当涉及到Nginx和JVM调优时,我们可以从两个方面来讨论。 首先是Nginx的调优。Nginx是一个高性能的Web服务器和反向代理服务器,以下是一些常见的Nginx调优方法: 1. 调整worker_processes和worker_connections:根据服务器的硬件配置和负载情况,适当调整worker_processes(工作进程数)和worker_connections(每个工作进程的最大连接数)参数,以提高并发处理能力。 2. 启用gzip压缩:开启gzip压缩可以减小传输的数据量,提高网站的响应速度。 3. 调整缓冲区大小:通过调整proxy_buffer_size、proxy_buffers和proxy_busy_buffers_size等参数,可以优化Nginx对后端服务器的请求和响应的缓冲区管理,提高性能。 4. 使用缓存:使用Nginx的缓存功能可以减轻后端服务器的负载,提高响应速度。可以通过配置proxy_cache和相关参数来启用缓存。 5. 负载均衡:通过配置upstream模块,可以实现Nginx的负载均衡功能,将请求分发到多个后端服务器上,提高系统的可用性和性能。 接下来是JVM调优JVM是Java虚拟机的缩写,以下是一些常见的JVM调优方法: 1. 调整堆内存大小:通过调整-Xms和-Xmx参数,可以设置JVM的初始堆大小和最大堆大小,以适应应用程序的内存需求。 2. 设置垃圾回收器:根据应用程序的特点和性能需求,选择合适的垃圾回收器,如Serial GC、Parallel GC、CMS GC或G1 GC,并通过相关参数进行配置。 3. 调整线程数:通过调整-Xss参数,可以设置线程栈的大小,以及通过调整-XX:ParallelGCThreads参数来设置并行垃圾回收线程数,以提高并发处理能力。 4. 监控和分析工具:使用JVM提供的监控和分析工具,如jstat、jconsole、jvisualvm等,可以实时监控JVM的运行状态和性能指标,帮助定位性能瓶颈和优化机会。 5. 代码优化:通过对代码进行优化,如减少对象的创建、避免过多的同步、合理使用缓存等,可以减少JVM的负载,提高性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值