0047JVM和tomcat优化

a 夏天 提交于 2020-01-08 13:40:25

java –help 查看标准参数

java –X 输出非标准的参数

 

jps查看正在运行的java进程

jps –l 列出正在运行的java进程的全路径

jinfo –flags 进程id   查看当前进程对应的jvm的所有运行参数

jinfo –flag 参数名 进程id  查看当前进程对应的jvm的指定参数的值

jstat命令查看对内存的使用情况

比如tomcat启动之后,可通过jstat –class  tomcat的进程id  来查看相关信息

 

具体操作可以如下,就知道怎么使用jstat了:

 

 

 

使用jstat可以对内存使用情况进行统计分析

 

jmap可以获得更加详细 信息,可以对内存使用情况进行汇总,对内存溢出的定位与分析。

jmap用法如下图:

 

 

 

如:

1)  jmap –heap 进程id  查看堆内存使用情况

2)  jmap –histo <pid> | more  查看所有对象,包括活跃的和非活跃的

3)  jmap –histo:live <pid> | more  查看活跃对象

4)  将当前堆内存使用情况dump到文件中进行分析,语法如下:

jmap –dump:format=b,file=dumpFileName <pid>

事例:jmap –dump:format=b,file=/tmp/testDump.dat  5967

 

 

jhat命令对dump文件进行分析,jhat用法如下图:

 

 

 

比如 jhat –port  9999 /tmp/testDump.dat

上述命令执行之后会提示Server is ready,就可以通过浏览器访问ip:端口进行访问,查看分析结果了,如192.168.225.131:9999,访问后在页面最下方有一个Object Query Language(OQL),可以进行查询,执行语句如下:

 

 

 MAT工具也可以对dump文件进行分析(全称Memory Analyzer Tool),是一个基于eclipse的内存分析工具,官网地址:https://www.eclipse.org/mat/

可以下载.zip安装文件,解压后双击MemoryAnalyzer.exe对工具进行启动

通过sz testDump.dat命令打开下载窗口,指定保存到windows的路径,将该文件传输到windows指定目录,

打开mat工具,左上角的file->open heap dump…打开刚才的文件,可以查看对象内存占用情况,以及类依赖关系

里边第2个页签还会显示出可以的占用内存较大的类或实例

 

小总结:jps查看运行的java进程,jinfo查看进程配置的jvm参数,jstat查看进程参数的使用情况,jmap分析与定位内存溢出的原因及导出dump文件。

导出dump文件之前需要用命令,堆dump文件进行分析可以使用jhat命令,也可以使用mat工具。

 

 

内存溢出相关思路:

可能导致内存溢出的原因:不断的将数据写入一个集合中、出现了死循环、读取超大的文件等等都可能导致内存溢出。

分析是否是正常内存溢出:如果是正常情况,就考虑加大内存;如果是非正常情况,就要修复Bug.

如何定位问题:借助jmap命令和mat工具

配置上参数,内存溢出时,自动dump出文件:-XX:HeapDumpOnOutOfMemoryError

 

jstack的使用:

例如服务器的CPU负荷突然增高、出现了死锁、死循环等,这个时候我们需要查看jvm的内部线程的执行情况,这时就需要借助jstack命令了,jstack的作用是将正在运行的jvm的线程情况进行快照,并且打印出来。

用法如下图:

 

 

 

比如:jstack <pid>

执行上述命令,就会显示各个线程的状态,所以还需要了解线程的各个状态代表什么意思

 

构造死锁的代码:

 

执行死锁代码后用jstack <pid>命令进行查看具体死锁的原因。

 

 

VisualVM工具:

VisualVM能够监控线程,内存情况,查看方法的CPU时间和内存中的对象,已被GC的对象,反向查看分配的堆栈(如500个String对象分别由哪几个对象分配出来的)。

如何启动VisualVM工具:在jdk的安装目录的bin目录下,找到jvisualvm.exe,双击打开即可。

该工具很强大,既可以查看本地jvm使用情况,也可以查看远程的jvm使用情况。

 

如果需要通过本地的VisualVM工具监控远程服务:

1)  在远程服务器配置JMX相关信息

 

 

 

2)在本地的VisualVM工具中添加远程服务的连接,然后添加JMX的连接。

 

 

--------------------------垃圾回收相关------------------------------------

垃圾回收算法:复制算法、标记清除算法、标记压缩算法、分代算法

 

标记清理算法:遍历全部对象,标记从根可达的对象;遍历全部对象,清除未被标记的对象;

缺点:1)两次遍历全部对象,效率极低;2)清理出来的内存空间不连续

且需要知道执行标记清除算法的时候,是需要先暂停应用程序的运行,标记清除算法才能运行的,因为从根进行遍历,遍历了部分的时候,假如有新的对象加入到了引用关系中,而这部分引用关系在之前已经遍历过了,这就会导致新加入的对象明明是可达的,应该被标记的,却没有被标记,而导致被垃圾回收了。

标记压缩算法:在标记清除算法的基础上做了改进,清除阶段不是简单的清除,而是将存活的对象压缩到内存的一端,然后清理边界以外的垃圾。

优点:压缩清理后,内存空间连续

缺点:比标记清除算法多了一步对象移动内存位置的步骤,对效率也有一定的影响。

复制算法:将内存空间分为两块,垃圾回收时,将存活对象复制到另一块空间中,然后清除当前空间,交换两个内存的角色,完成垃圾回收。

优点:1)速度快;2)内存连续

缺点:浪费空间

复制算法试用于新生代,因为对象存活时间短,大部分对象会被回收掉。

分代算法:

年轻代用复制算法;老年代用标记清除或者标记压缩算法。

 

 

-----------------垃圾收集器及内存分配---------------------

垃圾收集器分为多种:1)串行垃圾收集器;2)并行垃圾收集器;3)并发垃圾收集器(CMS);4)G1垃圾收集器。

串行垃圾收集器:是指使用单线程进行垃圾回收,垃圾回收时,只有一个线程在工作,并且java应用中所有线程都要暂停,等待垃圾回收完成。这种现象称为STW(Stop-The-World)。

一般在javaweb应用中是不会采用这种收集器的。

写程序进行测试:

 

 

如何设置垃圾收集器为串行垃圾收集器呢?

1)-XX:+UseSerialGC  指定年轻代和老年代都使用串行垃圾收集器

2)-XX:+PrintGCDetails  打印垃圾回收的详细信息

 

 

  

在打印出的垃圾回收信息中:

DefNew:表示使用的是串行垃圾收集器

4416k->512k(4928k):表示gc前,年轻代占用4416k内存,gc后占用512k内存,共分配4928内存。

4416k->1973k(15872k):表示gc前,堆内存共占用4416k,gc后堆内存共占用1973k,对内存共分配15872k

0.0026308secs:表示gc所占用的时间,单位为毫秒

 

并行垃圾收集器:在串行垃圾收集器的基础上做了改进,将单线程改为了多线程进行垃圾回收,并行垃圾收集器也会暂停应用程序,只是并行垃圾收集器速度更快些,暂停时间更短一些。

-XX:+UseParNewGC指定年轻代使用ParNew收集器(并行收集器的一种),老年代使用的依然是串行收集器。

打印出的信息中的ParNew就代表年轻代使用了并行垃圾收集器。

ParallelGC:工作机制和ParNewGC收集器一样,只是在此基础上,只是在此基础上,新增了两个和系统吞吐量相关的参数,使得器使用起来更加灵活。

-XX:UseParallelGC:年轻代使用ParallelGC垃圾收集器,老年带使用串行收集器。

-XX:UseParallelOldGC:年轻代使用ParallelGC垃圾收集器,老年代使用ParallelOldGC垃圾收集器。

-XX:GCTimeRatio  :值为0-100之间,默认为99,也就是垃圾回收时间不能超过1%

-XX:UseAdaptiveSizePolicy : 自适应GC模式,垃圾收集器将自动调整新生代、老年代参数,达到吞吐量、堆大小、停顿时间之间的平衡。一般用于手动调整参数比较困难的情况,让收集器进行自动调整。

打印出的日志信息:PSYoungGen说明使用了并行垃圾收集器,而后边显示的信息就是年轻代的垃圾收集信息。ParOldGen说明老年代也使用的并行垃圾收集器,后边跟的是老年代垃圾收集的信息。

 

CMS垃圾收集器:全称Concurrent Mar Sweep,是一款并发的、使用标记-清除算法的垃圾收集器,该收集器是针对老年代垃圾回收的。

-XX:UseConcMarkSweepGC进行设置

 

 

 

优点:垃圾回收的时候可以让应用程序继续运行。

日志信息分析:

Parnew说明年轻代用的是并行垃圾收集器

 

 

 

CMS垃圾收集器使用率比较高。

 

G1垃圾收集器(重要)

G1垃圾收集器是在jdk1.7中正式使用的全新的垃圾收集器。

G1的设计原则是简化JVM性能调优,开发人员只需要简单的三步即可完成调优:

1)  开启G1垃圾收集器

2)  设置堆的最大内存

3)  设置最大的停顿时间

 

G1中提供三种模式的垃圾回收:Young GC、Mixed GC 和 Full GC,在不同条件下被触发。

原理:

G1垃圾收集器相比于其他收集器而言,最大的区别在于取消了年轻代和老年代的物理划分,取而代之的是将堆划分为若干个区域(Region),这些区域中包含了逻辑上的年轻代、老年代。

好处:我们不用单独的对每个代的空间进行设置,不用担心每个代内存是否足够。

 

 

 

G1参数设置:

 

 

  

可视化GC日志分析工具

GC EASY可视化工具:是一款在线的可视化工具,网站地址:https://gceasy.io/

如下图,上传日志文件后,点击Analyze即可。

 

 

 

 

 

-----------------------tomcat优化----------------------

使用jmeter工具连接tomcat进行压力测试,可以测试吞吐量

使用gceasy分析日志文件

sz命令可打开下载窗口,将linux中的文件下载到windows系统中

 

1)       tomcat自身配置

2)       tomcat所运行的jvm虚拟机的调优

 

第一部分:调整tomcat自身的配置进行优化:

tomcat的server.xml中:

1)  去除协议为AJP的Connector,即下边的注释

<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

2)  配置线程池

<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"

        maxThreads="150" minSpareThreads="4" prestartminSpareThreads=”true”/>

3)  线程池中配置等待队列

<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"

      maxThreads="150" minSpareThreads="4" prestartminSpareThreads=”true” maxQueueSize=”100”/>

4)  更改tomcat的运行模式,即将connector中的protocol由HTTP/1.1改为org.apache.coyote.http11.Http11Nio2Protocol

Connector port="8080" protocol="org.apache.coyote.http11.Http11Nio2Protocol"

               connectionTimeout="20000"

               redirectPort="8443" URIEncoding="UTF-8"/>

注意:只有jdk1.8之后才能设置成Nio2的运行模式

第二部分:调整JVM参数进行优化:

1)  设置并行垃圾收集器

在catalina.sh中文件注释的后面加入虚拟机并行垃圾收集器的设置

JAVA_OPTS=”-XX:+UseParallelGC  -XX:+UseParallelOldGC”

2)  调整堆大小,还是修改catalina.sh

比如设置初始堆大小128,最大堆大小1024m,新生代堆大小64m,

最大的新生代大小256m

-Xms128m  -Xmx1024m  -XX:NewSize=64m  -XX:MaxNewSize=256m

即适当的调大堆内存的大小,可以减少gc次数

 

设置G1垃圾收集器:

 

 

 

 

 

小结:

对于tomcat的性能调优就是需要不断的对参数进行调整,然后进行测试,由于调整参数可能会将性能调好,也可能会调差,所以需要借助gc的可视化工具来查看情况,比如jmeter和easygc,然后再考虑继续堆哪些参数进行调整。

另外,除了调优tomcat参数和jvm参数外,还要考虑优化程序员编写的程序。

 

 

--------------------JVM字节码-------------------------

编写的.java文件都是需要javac命令编译成.class文件,jvm将.class文件中的字节码载入到jvm进行运行的。

 

通过javap命令查看class文件中的字节码。

.java中原文件内容如下:

public class Test1 {

         public static void main(String[] args) {

                   int a=2;

                   int b=5;

                   int c = b-a;

                   System.out.println(c);

         }

 

}

比如有一个编译好的class文件,名称为Test.class,可执行如下命令:

javap –v Test1.class > Test1.txt

javap 命令用法可通过如下形式查看:

 

 

 生成的文件大概分为4部分内容:

 

 

 

字段描述符:

 

 

 

方法描述符:

 

方法的解读:

 

 

i++和++i的处理流程如下:

 

 

 -----------------------java代码的优化---------------

1、  for循环中不调用list.size()

比如for(int i=0;i<list.size();i++)是不好的。

2、  尽量采用懒加载策略

比如:

String str2 ="ab";
if(i == 1){
    list.add(s1);
}

上边这种形式不好,尽量改为下边这种形式

if(i == 1){
    String str2 ="ab";
    list.add(s1);
}
3、 不要引用一些不用的类,或者创建一些不适用的对象,因为这都是需要占用空间资源的。
4、 使用数据库连接池和线程池等池技术
5、 容器初始化时尽量指定长度,防止容器扩容时造成的性能损耗
如:new ArrayList(15);
    new HashMap(32);
6、 for循环中不用字符串相加的形式,而是用StringBuilder.append,因为如果用字符串相加,由于jvm做了优化,所以循环内每次循环都会创建一个新的StringBuilder对象,而对象的创建和回收是很耗费性能的。
7、 对资源的关闭,分开使用try catch,否则如果连个写在一切,第一个写在try中的关闭资源出错了,那么第二个关闭资源就不会被执行。
 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!