JVM的复习: 看jvm的图形
GC是什么?
频繁收集Young区
较少收集Old区
基本不动Perm区
GC三大算法:
GC算法总体概述:
JVM在进行GC时,并非每次都对上面三个内存区域一起回收的,大部分时候回收的都是指新生代
GC按照回收的区域又分了两种类型,一种是普通GC(minor GC),一种是全局GC(major GC or Full GC),
普通GC(minor GC):只针对新生代区域的GC。
全局GC(major GC or Full GC):针对年老代的GC,偶尔伴随对新生代的GC以及对永久代的GC。
复制算法:MinorGC :年轻代中使用的是 MinorGC,这种GC算法采用的是复制算法(Copying)
年轻代中的GC,主要是复制算法
HotSpot JVM把年轻代分为了三部分:1个Eden区和2个Survivor区(分别叫from和to)。默认比例为8:1:1,一般情况下,新创建的对象都会被分配到Eden区(一些大对象特殊处理),这些对象经过第一次Minor GC后,如果仍然存活,将会被移到Survivor区。对象在Survivor区中每熬过一次Minor GC,年龄就会增加1岁,当它的年龄增加到一定程度时,就会被移动到年老代中。因为年轻代中的对象基本都是朝生夕死的(80%以上),所以在年轻代的垃圾回收算法使用的是复制算法,复制算法的基本思想就是将内存分为两块,每次只用其中一块,当这一块内存用完,就将还活着的对象复制到另外一块上面。复制算法不会产生内存碎片。
在GC开始的时候,对象只会存在于Eden区和名为“From”的Survivor区,Survivor区“To”是空的。紧接着进行GC,Eden区中所有存活的对象都会被复制到“To”,而在“From”区中,仍存活的对象会根据他们的年龄值来决定去向。年龄达到一定值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置)的对象会被移动到年老代中,没有达到阈值的对象会被复制到“To”区域。经过这次GC后,Eden区和From区已经被清空。这个时候,“From”和“To”会交换他们的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎样,都会保证名为To的Survivor区域是空的。Minor GC会一直重复这样的过程,直到“To”区被填满,“To”区被填满之后,会将所有对象移动到年老代中。
因为Eden区对象一般存活率较低,一般的,使用两块10%的内存作为空闲和活动区间,而另外80%的内存,则是用来给新建对象分配内存的。一旦发生GC,将10%的活动区间与另外80%中存活的对象转移到10%的空闲区间,接下来,将之前90%的内存全部释放,以此类推。
复制算法的缺点:
复制算法弥补了标记/清除算法中,内存布局混乱的缺点。不过与此同时,它的缺点也是相当明显的。
1、它浪费了一半的内存,这太要命了。
2、如果对象的存活率很高,我们可以极端一点,假设是100%存活,那么我们需要将所有对象都复制一遍,并将所有引用地址重置一遍。复制这一工作所花费的时间,在对象存活率达到一定程度时,将会变的不可忽视。 所以从以上描述不难看出,复制算法要想使用,最起码对象的存活率要非常低才行,而且最重要的是,我们必须要克服50%内存的浪费。
标记清除/标记整理算法:FullGC又叫MajorGC : 老年代一般是有标记清除或者标记清除与标记整理的混合实现
标记清除的原理图:
标记清除的缺点 :①效率比较低(递归与全堆对象遍历)而且在进行GC的时候,需要停止应用程序,这会导致用户的体验非常差劲。②这种方式清理出来的空闲是不连续的,并且是死亡对象也是随机出现在各个角落的,现在把他们清除后,内存的布局肯定会乱七八糟,为了应付这个,JVM又不得不维护一个内存的空闲列表,并且在分配数组对象的时候,寻找连续的内存空间有不好找
标记整理(Mark-Compact)的原理:
标记整理的算法 : 唯一的缺点就是效率不高,不进要标记多有存活对象,还要整理所有存活对象的引用地址,从效率上说,标记/整理算法要低于复制算法
小总结:
内存效率:复制算法>标记清除算法>标记整理算法(此处的效率只是简单的对比时间复杂度,实际情况不一定如此)。
内存整齐度:复制算法=标记整理算法>标记清除算法。
内存利用率:标记整理算法=标记清除算法>复制算法。
年轻代(Young Gen)
年轻代特点是区域相对老年代较小,对像存活率低。
这种情况复制算法的回收整理,速度是最快的。复制算法的效率只和当前存活对像大小有关,因而很适用于年轻代的回收。而复制算法内存利用率不高的问题,通过hotspot中的两个survivor的设计得到缓解。
老年代(Tenure Gen)
老年代的特点是区域较大,对像存活率高。
这种情况,存在大量存活率高的对像,复制算法明显变得不合适。一般是由标记清除或者是标记清除与标记整理的混合实现。
Mark阶段的开销与存活对像的数量成正比,这点上说来,对于老年代,标记清除或者标记整理有一些不符,但可以通过多核/线程利用,对并发、并行的形式提标记效率。
Sweep阶段的开销与所管理区域的大小形正相关,但Sweep“就地处决”的特点,回收的过程没有对像的移动。使其相对其它有对像移动步骤的回收算法,仍然是效率最好的。但是需要解决内存碎片问题。
Compact阶段的开销与存活对像的数据成开比,如上一条所描述,对于大量对像的移动是很大开销的,做为老年代的第一选择并不合适。
基于上面的考虑,老年代一般是由标记清除或者是标记清除与标记整理的混合实现。以hotspot中的CMS回收器为例,CMS是基于Mark-Sweep实现的,对于对像的回收效率很高,而对于碎片问题,CMS采用基于Mark-Compact算法的Serial Old回收器做为补偿措施:当内存回收不佳(碎片导致的Concurrent Mode Failure时),将采用Serial Old执行Full GC以达到对老年代内存的整理。
Tomcat之启动优化:
大概在catalina.sh文件的97行
修改内容如下:
export JAVA_OPTS="-server -Xms1600M -Xmx1600M -Xss512k -XX:+AggressiveOpts -XX:+UseBiasedLocking -XX:PermSize=128M -XX:MaxPermSize=256M -XX:+DisableExplicitGC -XX:MaxTenuringThreshold=31 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -Djava.awt.headless=true"
查看是否生效: ps -ef |grep tomcat
jmap -heap 进程号(tomcat的进程号);
显示的内容:
参数逐项说明:
1.-server
-server 启用jdk 的 server 版;
只要你的tomcat是运行在生产环境中的,这个参数必须添加
因为tomcat默认是以一种叫java –client的模式来运行的,server即意味着你的tomcat是以真实的production的模式在运行的,这也就意味着你的tomcat以server模式运行时将拥有:
更大、更高的并发处理能力,更快更强捷的JVM垃圾回收机制,可以获得更多的负载与吞吐量......
2.-Xms -Xmx
-Xms 初始堆大小;其初始空间(即-Xms)是物理内存的1/64,
-Xmx 最大堆大小; 最大空间(-Xmx)是物理内存的1/4。
-Xmn 年轻代堆内存大小,此处是(eden+ 2 survivor space),默认为物理内存的1/64.
可以利用JVM提供的-Xmn -Xms -Xmx等选项,
要加“m”说明是MB,否则就是KB了,在启动tomcat时会报内存不足。
把Xms与Xmx两个值设成一样是最优的做法
WHY?
==================================================
一般设置-Xms、-Xmx相等以避免在每次GC后调整堆的大小。因为默认空余堆内存小于40%
时,JVM就会增大堆直到-Xmx的最大限制;空余堆内存大于70%时,JVM会减少堆直到-Xms的
最小限制。
3.-Xss
是指设定每个线程的栈大小。一般设置不超过1M,要不然容易出现out of memory
4.-XX:+AggressiveOpts
自带魔法属性,从单词可以看出,积极的、生猛的,也即可以将最新版的JDK优化后的新特性自动注入
5.-XX:+UseBiasedLocking
启用一个优化了的线程锁,对于高并发访问很重要 ,太多的请求忙不过来它自动优化,对于各自长短不一的请求,出现的阻塞、排队现象,他自己优化
6.-XX:PermSize -XX:MacPermSize
-XX:PermSize设置Perm区的初始大小,默认是物理内存的1/64;
在数据量的很大的文件导出时,一定要把这两个值设置上,否则会出现内存溢出的错误。
-XX:MaxPermSize设置Perm区的最大值,默认是32M,建议达到物理内存的1/4。
存放的都是jvm初始化时加载器加载的一些类型信息(包括类信息、常量、静态变量等),这些信息的生存周期比较长,GC不会在主程序运行期对PermGen Space进行清理,所以如果你的应用中有很多CLASS的话,就很可能出现PermGen Space错误。
上述两个参数值存在于jdk1.7之前,1.8后就没有了。
7.-XX:MaxNewSize
-XX:MaxNewSize 设置最大的年轻代大小,默认是16M
-XX:NewSize 设置年轻代大小
8.-XX:+DisableExplicitGC
在程序代码中不允许有显示的调用”System.gc()”,避免内存的大起大落
忽略手动调用GC的代码使得 System.gc()的调用就会变成一个空调用,完全不会触发任何GC
9.-XX:MaxTenuringThreshold
-XX:MaxTenuringThreshold — 设置对象在新生代中存活的次数,设置垃圾最大年龄
1 如果设置为零的话,则年轻代对象不经过Survivor区直接进入年老代。对于年老代比较多的应用,可以提高效率。
2 如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概率。
这个值的设置是根据本地监控后得到的一个理想的值,不能一概而论原搬照抄。
默认为:15。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论。
10.-XX:+UseConcMarkSweepGC
使用CMS内存收集。即CMS gc,这一特性只有jdk1.5即后续版本才具有的功能,它使用的是gc估算触发和heap占用触发。
我们知道频频繁的GC会造面JVM的大起大落从而影响到系统的效率,因此使用了CMS GC后可以在GC次数增多的情况下,每次GC的响应时间却很短,比如说使用了CMS GC后经过jprofiler的观察,GC被触发次数非常多,而每次GC耗时仅为几毫秒
添加该参数表示启动并发标识-清理(Mark-Sweep)回收器(CMS)
11.-XX:+UseParNewGC
设置年轻代为并行收集,对年轻代采用多线程并行回收,这样收得快
12.-XX:+CMSParallelRemarkEnabled
在使用UseParNewGC 的情况下, 尽量减少mark 的时间,降低标记停顿
13.-XX:+UseCMSCompactAtFullCollection
在使用concurrent gc 的情况下, 防止 memoryfragmention,
对live object 进行整理, 使 memory 碎片减少。
14.-XX:LargePageSizeInBytes
指定 Java heap的分页页面大小
15.-XX:+UseFastAccessorMethods
get,set 方法转成本地代码
16.-XX:+UseCMSInitiatingOccupancyOnly
指示只有在 old generation 在使用了初始化的比例后concurrent collector 启动收集
17.-Djava.awt.headless=true
这个参数一般我们都是放在最后使用的,这全参数的作用是这样的,有时我们会在我们的J2EE工程中使用一些图表工具如:jfreechart,用于在web网页输出GIF/JPG等流,
在winodws环境下,一般我们的app server在输出图形时不会碰到什么问题,
但是在linux/unix环境下经常会碰到一个exception导致你在winodws开发环境下图片显示的好好可是在linux/unix下却显示不出来,因此加上这个参数以免避这样的情况出现。
18.-XX:+PrintGCDetails
打印GC详情
Tomcat之并发优化:
位置:
/opt/tomcat7/conf下的server.xml文件中节点的配置优化,记得先备份;
出厂默认:
优化:
标准版:
<Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol"
maxThreads="600"
minSpareThreads="100"
maxSpareThreads="500"
acceptCount="700"
connectionTimeout="20000"
redirectPort="8443" />
复杂版:
<Connector port="8080"
protocol="HTTP/1.1"
URIEncoding="UTF-8"
minSpareThreads="25"
maxSpareThreads="75"
enableLookups="false"
disableUploadTimeout="true"
connectionTimeout="20000"
acceptCount="300"
maxThreads="300"
maxProcessors="1000"
minProcessors="5"
useURIValidationHack="false"
compression="on"
compressionMinSize="2048"
compressableMimeType="text/html,text/xml,text/JavaScript,text/css,text/plain"
redirectPort="8443"/>
坑爹情况:
1.URIEncoding=”UTF-8”
使得tomcat可以解析含有中文名的文件的url,在server.xml文件的Connector标签中增加URIEncoding="UTF-8"属性,解决中文参数乱码问题。
minSpareThreads
最小备用线程数,tomcat启动时的初始化的线程数,默认10
maxSpareThreads
enableLookups
如果希望调用request.getRemoteHost()进行DNS查询,以返回远程客户的实际主机名,将enableLookups设为true。
如果希望忽略DNS查询,仅仅返回IP地址,设为false(这样提高了性能)。
缺省情况下,DNS查询是使能的。
一句话:是否反查域名,取值为: true 或 false 。为了提高处理能力,应设置为 false
disableUploadTimeout
disableUploadTimeout="true",
类似于Apache中的keeyalive一样,是否需要tomcat容器单独设置上传时间限制,
这里是不用,还是使用标准的,不去给上传的附件单独做超时设置。
connectionTimeout
connectionTimeout:网络连接超时,单位毫秒。设置为 -1 表示永不超时
acceptCount
acceptCount是当线程数达到maxThreads后,后续请求会被放入一个等待队列,这个acceptCount是这个队列的大小,如果这个队列也满了,就直接refuse connection
一句话:acceptCount:当处理任务的线程数达到最大时,接受排队的请求个数,默认100
maxThreads
maxThreads:最大线程数,即同时处理的任务个数,默认值为200
Tomcat使用线程来处理接收的每个请求。这个值表示Tomcat可创建的最大的线程数,
即最大并发数。
保守推荐:600---900
maxProcessors
minProcessors
在 Java中线程是程序运行时的路径,是在一个程序中与其它控制线程无关的、能够独立运行的代码段。它们共享相同的地址空间。多线程帮助程序员写出CPU最大利用率的高效程序,使空闲时间保持最低,从而接受更多的请求。
通常Windows是1000个左右,Linux是2000个左右。
useURIValidationHack
减少它对一些url的不必要的检查从而减省开销,为提高性能可以设置为false
compression
compression :设置是否开启GZip压缩
HTTP 压缩可以大大提高浏览网站的速度,它的原理是,在客户端请求网页后,从服务器端将网页文件压缩,再下载到客户端,由客户端的浏览器负责解压缩并浏览。相对于普通的浏览过程HTML,CSS,Javascript , Text ,它可以节省40%左右的流量。更为重要的是,它可以对动态生成的,包括CGI、PHP , JSP , ASP , Servlet,SHTML等输出的网页也能进行压缩,压缩效率惊人。
compressionMinSize
compressionMinSize="2048" 启用压缩的输出内容大小,这里面默认为2KB
compressableMimeType
compressableMimeType:哪些类型需要压缩,默认是text/html,text/xml,text/plain
超时控制:
修改conf/web.xml配置文件,设置session-timeout的值(单位:分钟)
Tomcat的内存优化:
1.
查看%TOMCAT_HOME%\logs文件夹下,日志文件是否有内存溢出错误
错误提示:java.lang.OutOfMemoryError: Java heap space :
导致的原因:
Tomcat默认可以使用的内存为128MB,在较大型的应用项目中,这点内存是不够的,有可能导致系统无法运行。
常见的问题是报Tomcat内存溢出错误,Out of Memory(系统内存不足)的异常,从而导致客户端显示500错误,
一般调整Tomcat的使用内存即可解决此问题。
public static void main(String[] args)
{
System.out.println(Runtime.getRuntime().maxMemory()/1024/1024+" M");
byte[] byteArray = new byte[1*1024*1024*650];
System.out.println("#######3");
}
windows环境下修改:
“%TOMCAT_HOME%\bin\catalina.bat”文件,在文件开头增加如下设置:
set JAVA_OPTS=-Dfile.encoding=UTF-8 -server -Xms1024m -Xmx2048m -XX:NewSize=512m -XX:MaxNewSize=1024m -XX:PermSize=256m -XX:MaxPermSize=256m -XX:MaxTenuringThreshold=10 -XX:NewRatio=2 -XX:+DisableExplicitGC
linux环境下修改:
“%TOMCAT_HOME%\bin\catalina.sh”文件,在文件开头增加如下设置:
JAVA_OPTS=-Xms2048m -Xmx2048m
错误提示:java.lang.OutOfMemoryError: PermGen space :
导致原因:
PermGen space的全称是Permanent Generation space,是指内存的永久保存区域,这块内存主要是被JVM存
放Class和Meta信息的,Class在被Loader时就会被放到PermGen space中,它和存放类实例(Instance)的Heap区域不同,GC(Garbage Collection)不会在主程序运行期对PermGen space进行清理,所以如果你的应用中有很CLASS的话,就很可能出现PermGen space错误,这种错误常见在web服务器对JSP进行pre-compile的时候。如果你的WEB APP下都用了大量的第三方jar, 其大小超过了jvm默认的大小(4M)那么就会产生此错误信息了。
windows环境下修改:
“%TOMCAT_HOME%\bin\catalina.bat”文件,在文件开头增加如下设置:
set JAVA_OPTS=-Xms64m -Xmx256m -XX:PermSize=128M -XX:MaxNewSize=256m - XX:MaxPermSize=256m
linux环境下修改:
“%TOMCAT_HOME%\bin\catalina.sh”文件,在文件开头增加如下设置:
export JAVA_OPTS=-Xms256m -Xmx256m -XX:PermSize=128M -XX:MaxNewSize=256m XX:MaxPermSize=256m