++ 优化 频繁new_系统优化怎么做-JVM优化之开篇

f8b001a0c12ad709c689031cea6569ec.png

前言

这一期主要介绍进行关于JVM一些概念及经验。后面分章节去讲述相关工具的基本使用。并对针对Tomcat,相关JVM相关主要参数做过一定说明。

优化优先级

整体来讲,系统优化应先优化架构及代码,来解决具体功能点效率问题。最后通过JVM监控工具来发现一些隐藏较为深入的问题。

相关情形

  1. 内存占用并不断增加, 系统压力大情况下Full GC频繁,系统出现卡顿
  2. 线程出现大量等待及死锁, CPU使用率过高, 系统响应慢
  3. 堆(heap)内存不足或类加载导致JVM Crash,系统宕机

出现以上情况,就得使用工具分析JVM来确定问题

JVM内存模型

JDK1.7及以下

4978b371cd02819da7416f886f441fd1.png
JDK1.8下,PermGen替换成Vm MetaSpace

Heap域

  • 全局被所有线程分享
  • 存所有对象及集合对象

方法域

  • 全局被所有线程分享
  • 存所有类的结构定义包含属性,方法及构造函数等

Thread1.N

  • 本地私有栈,一个线程一个栈
  • 保持着所有在Heap域的对象引用(4byte长度)
  • 存储本地局部变量的存储(基础数据类型),程序运行状态,方法返回值

内存泄漏的分类

  • 堆内存泄漏 - 比较常见
  • 持久代内存泄漏
  • 栈内存泄漏
  • 系统资源内存泄漏 -比较常见

线程相关知识

JVM线程状态迁移

1b65f8f931cd1626a367683a0d8ff231.png

线程状态

  • 初始化(New):初期创建,启动后则进入可执行状态
  • 可执行状态(Runnable): 只要获取CPU时间,则开始执行
  • 运行状态(Running): 正在使用CPU执行
  • 阻塞状态(Bloked)
等待阻塞(wait)
同步阻塞(synchronized)
睡眠阻塞(sleep)
Join阻塞: 等待join子线程结束后,主线程才能执行,将异步执行的线程合并为同步的线程
  • 结束状态: 线程执行完毕或者异常退出

性能监控关注点

  1. 系统线程总数
  2. 死锁线程 需要优先解决
  3. 线程Bloked总数数量

线程Bloked多的情况下,考虑对待处理数据进行分片,进行多通道,多线程处理提高系统性能

如果系统处理慢,但CPU占用一直很低,就需要梳理系统处理流程,串行处理该并行处理,并行处理流程提高并发来解决。

线程死锁定义

当两个或者多个线程尝试获取其他资源的锁,而每个线程又陷入无限等待其他资源锁的释放(相互等待),除非一个用户的进程被终止。

几个死锁场景

  • 两个线程相互调用Thread.join(), 导致互相等待同步结束。
慎用线程join操作

022c33b7770cb5306efa17fb0b48e3cf.png
线程相互锁,形成死锁
  • 当两个线程使用嵌套的同步块时,一个线程占用了另一个线程的必需的锁,互相等待时被阻塞,就有可能出现死锁。 也可能多个线程形成环状锁,比如线程A等待线程B,线程B等待线程C,线程C等待线程A。线程A为了检测死锁,它需要递进地检测所有被B请求的锁。从线程B所请求的锁开始,线程A找到了线程C,发现线程C请求的锁被线程A自己持有着。这是它就知道发生了死锁。

732ec287be02544ef5fa7a94eca86e9c.png
嵌套式死锁
  • MySql中两个线程同时对两条记录做先读多写操作

6983f6057f9a758e991275fd2c69fc44.png
MySql死锁

避免死锁

  • 安全状态
能找到一个分配资源的序列能让所有进程都顺利完成
  • 银行家算法
采用预分配策略检查分配完成时系统是否处在安全状态

检测死锁

  • VisualVM(或其他工具)
监控线程状态,如果出现死锁得到相关代码位置
  • 利用死锁定理化间资源分配图来分析死锁的存在
参见以下资料

优化Tomcat的JVM

/bin/catalina.sh

修改JAVA_OPTS参数 这里需要参照机器配置,对JVM进行参数优化

JDK1.7

JAVA_OPTS="-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms512m -Xmx1024m -XX:NewSize=512m -XX:MaxNewSize=1024M -XX:PermSize=1024m -XX:MaxPermSize=1024m -XX:+DisableExplicitGC"

JDK1.8

JAVA_OPTS="-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms1024m -Xmx1024m -XX:NewSize=512m -XX:MaxNewSize=1024M -XX:+DisableExplicitGC"

1.8 版本中已经没有PermSize、MaxPermSize

JAVA8里对metaspace可以在小范围自动扩展永生代避免溢出。

参数说明

  • -Djava.awt.headless
没有设备、键盘或鼠标的模式。
  • -Dfile.encoding
设置字符集
  • -server
vm的server工作模式,对应的有client工作模式。使用“java -version”可以查看当前工作模式
  • -Xms1024m
初始Heap大小,使用的最小内存
  • -Xmx1024m
Java heap最大值,使用的最大内存 经验: 设置Xms大小等于Xmx大小
  • -XX:NewSize=512m
表示新生代初始内存的大小,应该小于 -Xms的值
  • -XX:MaxNewSize=1024M
表示新生代可被分配的内存的最大上限,应该小于 -Xmx的值
  • -XX:PermSize=1024m
设定内存的永久保存区域,内存的永久保存区域,VM 存放Class 和 Meta 信息,JVM在运行期间不会清除该区域

程序加载很多class情况下,超出PermSize情况下

JDK1.7会抛出java.lang.OutOfMemoryError: PermGen space异常

JDK1.8下会抛出 ERROR: java.lang.OutOfMemoryError: Metadata space 异常

  • -XX:MaxPermSize=1024m
设定最大内存的永久保存区域 经验: 设置PermSize大小等于MaxPermSize大小
  • -XX:+DisableExplicitGC
自动将System.gc()调用转换成一个空操作,即应用中调用System.gc()会变成一个空操作,避免程序员在代码里进行System.gc()这种危险操作。System.gc() 除非是到了万不得以的情况下不使用,都交给JVM吧

其他优化参数

  • -XX:SurvivorRatio=2
年轻代中Eden区与Survivor区的大小比值
  • -XX:ReservedCodeCacheSize=256m
保留代码占用的内存容量,无大的影响
  • -Xss1024k
单个线程堆栈大小值,减少这个值可以生成更多线程,但操作系统对于一个进程内的线程数是有限制的,经验值在3000-5000左右
  • -XX:+CMSParallelRemarkEnabled
CMS 垃圾回收算法,对响应时间的重要性需求 大于 对吞吐量的要求,能够承受垃圾回收线程和应用线程共享处理器资源,并且应用中存在比较多的长生命周期的对象的应用
  • -XX:+UseCMSCompactAtFullCollection
在使用concurrent gc 的情况下, 防止 memoryfragmention, 对live object 进行整理, 使 memory 碎片减少。
  • -XX:+UseCMSInitiatingOccupancyOnly
在FULL GC的时候, 对年老代的压缩。CMS是不会移动内存的, 因此这个非常容易产生碎片, 导致内存不够用, 因此, 内存的压缩这个时候就会被启用。 增加这个参数是个好习惯。可能会影响性能,但是可以消除碎片。
  • -XX:CMSInitiatingOccupancyFraction=60
使用cms作为垃圾回收, 使用60%后开始CMS收集
  • -XX:+UseGCOverheadLimit
用来限制使用内存,如果不做控制,可能会报出 java.lang.OutOfMemoryError: GC overhead limit exceeded
  • -XX:+UseConcMarkSweepGC
使用CMS内存收集
  • -XX:+UseParNewGC
设置年轻代为并行收集
  • -XX:+HeapDumpOnOutOfMemoryError
  • -XX:HeapDumpPath=/x/dump_tomcat.hprof
JVM会在遇到OutOfMemoryError时拍摄一个“堆转储快照”,并将其保存在一个文件中。
  • -Xloggc:/xx/gc_tomcat.log
gc的日志,如果该日志中出现频繁的Full GC就是有相关的系统问题,如果很少,说明暂时还算正常
  • -XX:+PrintGCDateStamps
输出GC的时间戳(以基准时间的形式)
  • -XX:+PrintGCDetails
输出GC的日志格式
  • -Dnetworkaddress.cache.ttl=60
  • -Dsun.net.inetaddr.ttl=60
设置DNS缓存时间
  • -DautoStartup=false
  • -Dsun.net.client.defaultConnectTimeout=60000
连接建立超时时间
  • -Dsun.net.client.defaultReadTimeout=60000
内容获取超时设置
  • -Djmagick.systemclassloader=no
是否生成缩略图的一个框架的配置
  • -Djava.security.egd=file:/dev/./urandom

最佳实践

export JAVA_OPTS="-server -showversion -Xms2000m -Xmx2000m -Xmn500m -XX:PermSize=256m -XX:MaxPermSize=256m -XX:SurvivorRatio=2 -XX:ReservedCodeCacheSize=256m -Xss1024k -Djava.awt.headless=true -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=60 -XX:+UseGCOverheadLimit -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tomcat_path/logs/dump_tomcat.hprof -Xloggc:/tomcat_path/logs/gc_tomcat.log -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintGCDetails -Dnetworkaddress.cache.ttl=60 -Dsun.net.inetaddr.ttl=60 -DautoStartup=false -Dsun.net.client.defaultConnectTimeout=60000 -Dsun.net.client.defaultReadTimeout=60000 -Djmagick.systemclassloader=no -Djava.security.egd=file:/dev/./urandom -Dfile.encoding=UTF-8"

常见JVM异常

  • java.lang.OutOfMemoryError: Java heap space —-JVM Heap(堆)溢出

JVM 在启动的时候会自动设置 JVM Heap 的值,其初始空间(即-Xms)是物理内存的1/64,最大空间(-Xmx)不可超过物理内存。可以利用 JVM提供的 -Xmn -Xms -Xmx 等选项可进行设置。Heap 的大小是 Young Generation 和 Tenured Generaion 之和。在 JVM 中如果 98% 的时间是用于 GC,且可用的 Heap size 不足 2% 的时候将抛出此异常信息。

解决方法:手动设置 JVM Heap(堆)的大小。

  • java.lang.OutOfMemoryError: PermGen space —- PermGen space溢出。
jdk1.8 抛出 ERROR: java.lang.OutOfMemoryError: Metadata space 异常

PermGen space 的全称是 Permanent Generation space,是指内存的永久保存区域。为什么会内存溢出,这是由于这块内存主要是被 JVM 存放Class 和 Meta 信息的,Class 在被 Load 的时候被放入 PermGen space 区域,它和存放 Instance 的 Heap 区域不同,sun 的 GC 不会在主程序运行期对 PermGen space 进行清理,所以如果你的系统载入很多 CLASS 的话,就很可能出现 PermGen space 溢出。

解决方法: 手动设置 MaxPermSize 大小

  • java.lang.StackOverflowError —- 栈溢出

栈溢出了,JVM 依然是采用栈式的虚拟机。函数的调用过程都体现在堆栈和退栈上了。调用构造函数的 “层”太多了,以致于把栈区溢出了。通常来讲,一般栈区远远小于堆区的,因为函数调用过程往往不会多于上千层,而即便每个函数调用需要 1K 的空间(这个大约相当于在一个 C 函数内声明了 256 个 int 类型的变量),那么栈区也不过是需要 1MB 的空间。通常栈的大小是 1-2MB 的。 通常递归也不要递归的层次过多,很容易溢出。

总结

本章主要讲了一些核心知识,主要为了让大家了解系统优化到底优化和解决什么问题,什么是优化的目标。后续章节会讲到tomcat的JMX配置,VisualVM,Tprofile等工具的使用。

思考题

JVM线程有哪些状态,这些线程大多处于什么样的状态分布, 我们可以称系统运行是健康的。

Python网络爬虫与推荐算法新闻推荐平台:网络爬虫:通过Python实现新浪新闻的爬取,可爬取新闻页面上的标题、文本、图片、视频链接(保留排版) 推荐算法:权重衰减+标签推荐+区域推荐+热点推荐.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值