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中的关闭资源出错了,那么第二个关闭资源就不会被执行。