jvm性能调优并用Jmeter测试

java内存结构
在jvm和jmm这篇文章里面 讲了jvm内存模型,但是没有对 栈区进行细分,这里小编参考一些资料画了一张图,细分一下栈空间
在这里插入图片描述
同时方法区在java8里面改名成永久区

堆内存的构成
在这里插入图片描述
eden 伊甸园
survivor 幸存者
当我们实例化对象的时候 占用空间特别大的对象 会被放入老年代 ,其他普通的对象 会被放入新生代,内部使用GC 垃圾回收器 来进行整理和收集的,如果经历过第一次GC之后,eden当中的某个对象并没有被回收(因为一个引用老指向它),那么此时该没有被回收对象就会从eden区进入其中的一个survivor区(这里以左边的为例),那么在第二次GC的时候,(该survivor区(左边的)的对象会被拷贝到另外一个survivor区(右边的),同时第二次未被回收的对象,也会进入survivor区(右边的)),再经历一次GC,如果eden区还有对象没有被回收,(此时由于左边的survivor区中所有的对象已经被复制到右边的survivor区,那么右边的survivor中的内存会被复制到左边,同时没有被回收的对象放入左边的survivor,再下一次GC也会是同样的过程,循环往复.
两个survivor也可以叫做from区和to区,from区和to区互相的角色总是在不停的转换,(内存被拷贝来 拷贝去…)每个时刻 都会有一个survivor区为空, 书中说是15次复制,如果经历了很多次的GC survivor区中还有没被回收掉 就会被放入老年代,大概理解一下复制的过程
堆内存参数调整
所谓的调优 主要是两块 第一块 对内存的调整,第二块 垃圾收集的调整

用visualVM观察虚拟机堆信息
垃圾收集算法
**什么是垃圾 **
没有任何引用指向的对象 被叫做垃圾 画个图 简单说明
在这里插入图片描述
不是完全对,在我们的程序里,如果右a,b两个对象 内部有一个引用指向a,也有一个引用指向b,但是 除了这两个引用以外,没有人指向这两对象,那么这两对象叫做一堆垃圾,此外 还有环形垃圾 a->b->c->d ,d->a
GC如何确定垃圾
用引用计数 这样不行 因为会有循环引用的问题
采用正向可达的算法来确定垃圾,(从roots对象计算可以到达的对象,都不是垃圾,剩下的都是垃圾)
回收算法
Mark-sweap 标记清除
在这里插入图片描述
通过正向可达一系列操作,找到垃圾 并把它们标记,再由垃圾回收器(另外一个线程)回收(清除)清除后 再把不可用的内存 标记为可用,但是这样会导致内存不连续 碎片化,如果一个特别大的对象 需要连续的空间来存储,此时 就要发生父GC (把所有可食用的对象复制到一端区 进行压缩,腾出空间来存放此大对象)这样效率太低
Copying(复制算法)
在这里插入图片描述
比例问题,eden:survivor 一般为8:1:1
eden+survivor:tenured 1:3 或3:8 或1:2
区域的调整要根据具体的应用来,

Mark-compact(标记压缩)
标记压缩,把不是垃圾的内存标记起来,都往一端压缩,压缩到一端后 腾出空间放其他对象,这种算法 效率比复制算法略低,一般被用于老年代中(因为在老年代中的对象一直不消失,产生的垃圾比较少 采用标记压缩效率比较高),也就是说在新生代eden和survivor之间用的是copy的算法, (因为新生代中 基本上大部分的对象都会被回收,剩下的对象放入survivor区 你占用内存较少)
在这里插入图片描述
jvm参数

– 默认参数
–X 非标准
–XX 不稳定参数

垃圾收集器
串行收集器 单线程收集 XX:+UseSerialGC
并行收集器(paraillel) 多线程处理 XX:+UseParallelGC
不过每次垃圾收集完 虚拟机需要停顿,
并发收集器 XX:+UseConcMarkSweepGC (cms收集器,除了多线程之外 并发来做,分成好多个区) 并发量大,停顿时间短,
G1 收集器 --XX:+UseG1GC (被java9作为默认的收集器)
不仅停顿短,同时并发量大

不开逃逸分析,就不能分配到栈上

堆设置
-Xms 初始堆大小
-Xmx 堆最大大小
-Xss 线程栈大小
-XX:NewSize=n 设置年轻代大小
-XX:NewRatio=n 设置年轻代和老年代的比值, 如3 表示年轻代和老年代的比值为1:3 年轻代占整个年轻代老年代之和的1/4
-XX:SurvivorRatio=n
年轻带中两个survivor的比值,如3 表示Eden:survovor = 3:2 一个survovor占整个新生代的1/5 如8 表示Eden:survivor =8:2 survivor占整个新生代的1/10
-XX:MaxPermsize=n 设置持久代大小

收集器设置
-XX:+UseSerialGC 设置使用串行收集器
-XX:+UseParallelGC 设置使用并行收集器
-XX:+UseConcMarkSweepGC 设置使用并发收集器

垃圾回收统计信息
-XX:+printGC
-XX:+printGCDetails
-Xloggc:filename 将jvm信息打印进文件
java对象的分配
其实jvm对于对象 首先会在栈空间分配和线程本地分配(TLIB thread local allocation buffer 每个线程默认在eden区申请1%的空间,由于这部分空间是共享的,所以要加锁来控制访问),如果栈空间中分配不了,线程本地也分配不了 首先会检查一下自身是否为大对象 ,如果不是特别大,会在新生代eden区分配,如果特别大,会在老年代中分配,jvm默认开启栈上优化(逃逸分析 和标量替换 栈上分配的效率特别高,方法执行结束自会释放,不需要GC)
在这里插入图片描述
在这里插入图片描述
我们可以在启动参数中设置一下参数关闭 栈优化及线程本地分配 -为关闭

-XX -DoEscapeAnalysis -XX:-EliminateAllocations -XX :-UseTLAB
+为开启
-XX+printGC 打印GC信息
我们可以写一个小程序,实例化一亿个对象,并对比一下栈优化之前和优化之后的效率,图中显示的是优化以后的GC信息
在这里插入图片描述
在这里插入图片描述
同时-XX+printGCDetails 打印堆内内具体信息
在这里插入图片描述
现在我们使用一下线程本地缓存,
在这里插入图片描述
在这里插入图片描述
我们会发现eden区的的内存占用量少了,正好说明了TLIB其实是使用eden区的

在这里插入图片描述
典型tomcat优化配置
set JAVA_OPTS=
-Xms=4g 初始堆大小4g
-Xmx=4g 堆最大大小4g
-Xss512k 线程栈512k
-XX:+AggressiveOpts 优化 进攻性的 凡是虚拟机里面能用到的优化选项都用上
-XX:+UseBiasedLocking 偏置锁 一个锁没有被锁定的情况下 线程去访问的时候一个优化的选项
-XX:MaxPermSize=300M 永久区的大小 最大给300M
-XX:+DisableExplicitGC 关闭显示调用gc 意思是不让显示的调用gc
无监控不调优,一定要使用监控工具
在这里插入图片描述

首先 先确定服务器上有几个虚拟机,服务器的内存大小 ,然后确定给tomcat多大的内存,比如就跑了一个tomcat 那么只需把jvm设置为最大内存
使用Jmeter进行测试
下载Jmeter,启动并测试,这里我们以本地tomcat为例,不部署任何项目,访问一下example jsp页面 并启动2000个线程 测试一下吞吐量
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
并添加监控图表
在这里插入图片描述
我们先不加任何调优参数 看一下服务器每秒处理http请求的吞吐量
在这里插入图片描述
下面 我们添加jvm参数,然后再启动tomcat 测试并发
在这里插入图片描述
在这里插入图片描述
关于此题,《深入理解java虚拟机》有关于OOM(OutOfMemory)问题的解释
A:属于运行时常量池导致的溢出,设置-XX:MaxPermSize可以解决这个问题,
B:属于堆空间不足导致的错误,问题比较少见,解决方式和C相同,
C:属于java堆内存问题,一般的手段是通过内存映像分析工具,对Dump出来的堆转储存快照进行分析,重点是确认内存中的对象是否是有必要的,也就是要判断是出现了内存泄漏,还是出现了内存溢出,如果是内存列楼,通过工具检查泄露对象打GC Roots的引用链信息,可以准确的确定出泄露代码的位置,不存在泄露,就应该检查虚拟机的堆参数,如果可以继续调大,可以设置-Xmx解决问题

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值