JVm调优总结

1 jvm虚拟机中有数据类型分为两种,基本类型和引用类型,基本类型包括byte,short,int,long,float,double,boolean,char;引用类型包括类类型,接口类型,和数组

2堆和栈是程序运行的关键,栈是运行时的单位,解决运行是的问题,堆是存储的单位,解决存储的问题。

3一个线程需要一个独立的线程栈,而所有的线程共享堆中的数据。

1堆和栈的分离,使得堆中的数据可以被多个线程共享,一方面提供了有效的数据交互方式,另一方面,堆中的共享的常量和缓存可以被所有栈访问,节省了空间。

2栈因为运行需要,比如保存系统运行的上下文,需要进行地址划分,只能向上增长,限制栈存储内容的能力。而堆中的内容可以根据需要动态增长。

1在java中main就是栈的起点,也是程序的起始点。

2堆中存放的是对象。栈中存放的是基本数据类型和对象的引用。对象的大小不可估计。而基本数据和对象的引用对应的大小是固定的的,占用的空间小。

3程序运行永远在栈中进行,传递参数是只存在传递基本数据和对象的引用的副本,而不是传递对象本身。在栈中,基本类型和引用的处理是一样的,都是传值。

下面是一个很好的列子,别的地方找的。

  1. class  Test
  2. {
  3.   public  static  void  main(String  args[])
  4.   {
  5.     int  val;
  6.     StringBuffer  sb1, sb2;
  7.  
  8.     val = 10;
  9.     sb1 = new  StringBuffer ("apples" );
  10.     sb2 = new  StringBuffer ("pears" );
  11.     System .out.println("val is "  + val);
  12.     System .out.println("sb1 is "  + sb1);
  13.     System .out.println("sb2 is "  + sb2);
  14.     System .out.println("" );
  15.  
  16.     System .out.println("calling modify" );
  17.     // 按值传递所有参数
  18.     modify(val, sb1, sb2);
  19.     System .out.println("returned from modify" );
  20.     System .out.println("" );
  21.  
  22.     System .out.println("val is "  + val);
  23.     System .out.println("sb1 is "  + sb1);
  24.     System .out.println("sb2 is "  + sb2);
  25.   }
  26.  
  27.   public  static  void  modify(int  a, StringBuffer  r1,
  28.                             StringBuffer  r2)
  29.   {
  30.       System .out.println("in modify..." );
  31.       a = 0;
  32.       r1 = null ;  //1
  33.       r2.append(" taste good" );
  34.       System .out.println("a is "  + a);
  35.       System .out.println("r1 is "  + r1);
  36.       System .out.println("r2 is "  + r2);
  37.   }
  38. }
  39.  
Java  应用程序的输出
  1.  
  2. val is 10
  3. sb1 is apples
  4. sb2 is pears
  5.  
  6. calling modify
  7. in modify...
  8. a is 0
  9. r1 is null
  10. r2 is pears taste good
  11. returned from modify
  12.  
  13. val is 10
  14. sb1 is apples
  15. sb2 is pears taste good
  16.  


这段代码声明了三个变量:一个整型变量和两个对象引用。设置了每个变量的初始值并将它们打印出来。然后将所有三个变量作为参数传递给  modify  方法。 

modify  方法更改了所有三个参数的值:  

将第一个参数(整数)设置为  0 。  
将第一个对象引用  r1  设置为  null 。  
保留第二个引用  r2  的值,但通过调用  append  方法更改它所引用的对象(这与前面的  C++  示例中对指针  p  的处理类似)。 

当执行返回到  main  时,再次打印出这三个参数的值。正如预期的那样,整型的  val  没有改变。对象引用  sb1  也没有改变。如果  sb1  是按引用传递的,正如许多人声称的那样,它将为  null 。但是,因为  Java  编程语言按值传递所有参数,所以是将  sb1  的引用的一个副本传递给了  modify  方法。当  modify  方法在  //1  位置将  r1  设置为  null  时,它只是对 sb1  的引用的一个副本进行了该操作,而不是像  C++  中那样对原始值进行操作。 

另外请注意,第二个对象引用  sb2  打印出的是在  modify  方法中设置的新字符串。即使  modify  中的变量  r2  只是引用 sb2  的一个副本,但它们指向同一个对象。因此,对复制的引用所调用的方法更改的是同一个对象。
 
java对象的大小
一个空Object的大小为8字节。
引用类型
强引用,软引用,弱引用
强引用 我们在生成对象是Java虚拟机生成的引用,强引用环境下不会被垃圾回收,而软引用和弱引用一般做为缓存来使用。弱引用一定会被垃圾回收。软引用在内存紧张的时候会被垃圾回收。我们一般都是用的强引用。
 
基本垃圾回收算法
引用技术:此对象有一个引用,增加一个计数,减少一个就计数减一,垃圾回收时,收集计数为0的对象。致命的无法处理循环引用问题。
标记-清除:从引用节点开始标记所有被引用的对象,遍历整个堆,清除没有引用的对象。
复制:把内存划分为两个部分,垃圾回收时,遍历使用的部分,把正在使用的对象复制到另一个区域,清空原来的区域。算法缺点,要使用两倍的内存空间。
标记-复制:结合复制和清楚的优点,标记正在使用的对象,把正在使用的对象压缩到堆的其中一块。
 
按分区对待的方式划分
增量收集:在应用进行的时候同时回收垃圾。
对象建立和对象回收问题
1 垃圾回收前,暂停对象建立。清理完成之后再运行,应用太大的时候暂停时间太长。
2 并发垃圾回收:回收和建立同时进行,对性能要求提高,而且碎片不好整理。
 
分代收集:把对象分为年轻代,年老代,持久代,不同生命周期采用不同的垃圾回收算法。现在的垃圾回收器都是按照此算法。
把不同的对象放在不同的代上,分别对待,采取不同的回收算法,提高效率。
新生成的对象都放在年轻代,年轻代分为三个区,存活时间很久的会进入老年代,老年代中存活的都是生命周期很长的,持久代中存放的静态文件如java类,方法。
 
java回收的起点是一些根对象(java栈,静态变量,寄存器。。)对 应  标 记-清除。从起点遍历,找到正在使用的对象,没有使用的就回收。
复制,和标记-整理方式都可以清理碎片。
 
垃圾回收分两种类型 一种scavenge gc主要处理年轻代,把第一个去中没存活的对象回收,在把存活的放在第二个区,
还有一种对整个堆进行清理,在年老代被写满,持久代写满用到full gc,full gc很慢,少用。
 
按照系统线程分
串行收集:单处理器机器,也可以用在小数据量(100m)情况的多处理器机器。可以使用-XX:+UseSerialGC打开。
并行收集:使用多线程垃圾回收工作,速度快,效率高。对老年代还是默认单线程,可以使用-XX+UserParallelGC打开,使用-XX:+UseParallelOldGC打开老年代多线程。使用-XX:ParallelGCThreads=<n>设置并行的线程数,N可以和机器处理器数量相等。最大垃圾回收暂停,-XX:MaxGCPauseMillis=<N>,N为毫秒,指定这个值会减少应用的吞吐量。   吞吐量是垃圾回收时间和非垃圾回收时间的对比,通过-XX:GCTimeRatio=<N>设定,公式为1/(1+n),如n=19,则为5%,默认为1%。适合多CPU,对应用响应时间无要求的中,大型应用。
并发收集:前面两个在垃圾回收是会暂停整个运行环境,适合对响应要求比较高的中,大规模应用。使用-XX:UseConcMarkSweepGC打开。对处理器要求很高。
 
以下配置主要针对分代垃圾回收算法
JVM中堆的大小设置有三个限制,相关操作系统的数据模型(32位64位),系统可用虚拟内存限制,机器物理内存限制,32位系统一般1.5到2G
典型设置
-Xmx3550m:设置JVM最大可用内存3550M,
-Xms3550m:JVM初始内存为3550m,可以与和第一个相同,避免垃圾回收完成之后JVM重新分配内存。
-Xmn2g:设置年轻代大小2G;整个堆大小=年轻代+年老代+持久代大小,持久代一般固定为64m,增大年轻代,将影响年老代的大小,对系统性能影响很大,Sun公司推荐为整个堆的3/8;
-Xss128K;设置每个线程的栈的大小,减少这个值能生成更多的线程,但操作系统对线程的个数有限制的,一般为3000-5000;
 
并行收集器适合对吞吐量优先的系统,
并行收集器适合对响应时间优先的系统,并发收集器不会对内存空间进行压缩整理。
 
辅助打印信息,以供调试
-XX:+PrintGC;-XX:+PrintGCDetails;-XX:+PrintGCTimeStamps可以和前两个混合使用。
 
 
常见配置
堆配置
-Xms:初始堆大小
-Xmx:最大堆大小
-XX:NewSize=N:设置年轻代大小
-XX:NewRatio=N设置年轻代和老年代的比例  比如n=3;表示年轻代和老年代 1:3
-XX:SurvivorRatio=n  年轻代中Eden区和Survivor区的比值,Survivor有两个区,n=3,表示一个Survivor区和Eden区比是1:3,整两个Survivor区和Eden区为2:3
-XX:MaxPermSize=n 设置持久代的大小
 
收集器设置
 
-XX:+UseSerialGC 设置串行收集器
-XX:+UseParallelGC 设置并行收集器
-XX:+UseParallelOldGC 设置老年代并行收集器
-XX:+UseConcMarkSweepGC 设置并发收集器
 
垃圾回收统计信息
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:filename
 
 
并行收集器设置
-XX:ParallelGCThread=n设置并行收集器收集时使用的CPU数,并行收集线程数
-XX:MaxGCPauseMillis=n设置并行收集的最大暂停时间
-XX:GCTimeRatio=n设置垃圾回收占程序运行时间的百分比 公式为1/(1+n)
 
并发收集器设置
-XX:+CMSIncrementalMode:设置为增量模式,使用于单CPU
 
年轻代大小的设置
响应时间和吞吐量优先的都应该尽可能的设置大一点
年老代大小的设置
吞吐量优先的一般都是一个很大的年轻代和一个较小的年老代,这样可以尽可能的回收短期对象,减少中期对象,年老对象都是存放的生命周期长的对象。
响应时间优先的要考虑很多因素,要看情况而定
 
年老代可能会出现碎片,需要如下配置
-XX:+UseCMSCompactAtFullCollection:使用并发收集器时,开启对年老代的压缩
-XX:CMSFullGCsBeforeCompaction=0:上面设置开启的情况下,设置多少Full Gc后,对年老代进行压缩。
 
 
JVM调优工具
 
Jconsloe JDK自带,性能要求低
JProfiler:功能强大,需付费
VisualVM:JDK自带,功能强大,推荐。
 
一般都有这些功能
1 堆信息查看(堆空间的大小分配(年轻代,年老代,持久代))
    可以解决如下问题  查看 年老代,年轻代大小划分是否合理 ,内存泄漏,垃圾回收算法是否合理
2 提供即时垃圾回收功能
3 垃圾监控(长时间监控回收情况)
4 查看堆内存,对象信息查看:数量,类型。
5 对象引用情况查看
 
6 线程监控,线程数量,各个线程都处在什么情况下。
7 热点分析 CPU热点:检查系统哪些方法占用的大量CPU时间。 内存热点:检查哪些对象在系统中数量最大,可以根据找到的热点,有针对性的对系统进行优化
 8 内存泄漏检查(年老代堆空间被沾满,持久代被占满)。
 
原文地址:https://www.cnblogs.com/chslch/p/4855919.html