JVM 结构概览
ClassLoader概述
public class MyApp {
public static void main(String[] args) {
//输出null,即BootstrapClassLoader,是由c++启动的
System.out.println(MyApp.class.getClassLoader().getParent().getParent());
//输出sun.misc.Launcher$ExtClassLoader@15db9742
System.out.println(MyApp.class.getClassLoader().getParent()); //ExtClassLoader
//输出sun.misc.Launcher$AppClassLoader@73d16e93
System.out.println(MyApp.class.getClassLoader());
//ClassLoader是一个抽象类,可以自定义一个类加载器
}
}
Execution Engine 概述
Native 概述
public synchronized void start() {
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
//java.lang.Thread类中的开启新线程的方法,是由操作系统完成的
private native void start0();
Program Counter Register
又叫PC寄存器。是线程私有的,可以理解为一个指针,用来存储指向下一条指令的地址例如入栈和出栈顺序时怎么建立起来的,就是靠它。
Method Area 方法区
方法区是被所有线程共享,存放所有类的信息、常量池、static修饰的方法和变量
Java stack 栈
heap 堆
如果新生区设置的内存比较小(1M),而new出来的对象直接就超过了新生区(5M),这时候该对象会被直接放进养老区(新生区都放不下,幸存区更放不下)
如果新生区设置的内存比较小(5M),循环10次new出1M的对象,当循环到第4次的时候,会触发GC回收,但是扔会有部分对象进入养老区(幸存区不足1M,刚触发GC回收,空间还没腾出来)
public static void main(String[] args) {
Runtime.getRuntime().maxMemory(); //jvm 可使用的最大堆内存,即-Xms
Runtime.getRuntime().totalMemory(); //jvm 当前使用的堆内存总量,即-Xmx
Runtime.getRuntime().freeMemory() //jvm 当前可用堆内存大小
}
jvm溢出测试
1. eclipse 安装mat插件或者下载独立版的https://www.eclipse.org/mat/downloads.php
2. 创建测试类
public class JVMTest {
byte[] byteArray = new byte[1*1024*1024];//1MB
public static void main(String[] args) {
List<JVMTest>list = new ArrayList<JVMTest>();
int count = 0;
try {
while(true)
{
list.add(new JVMTest());
count = count + 1;
}
} catch (Throwable e) {
System.out.println("*********count="+count);
e.printStackTrace();
}
}
}
3. 设置jvm参数,启动测试类
-XX:+HeapDumpOnOutOfMemoryError 内存溢出时生成快照文件
-XX:HeapDumpPath=d:/ 快照存放路径
JVM 参数
1. 标准参数(-),所有的JVM实现都必须实现这些参数的功能,而且向后兼容;
2. 非标准参数(-X),默认jvm实现这些参数的功能,但是并不保证所有jvm实现都满足,且不保证向后兼容;
3. 非Stable参数(-XX),此类参数各个jvm实现会有所不同,将来可能会随时取消,需要慎重使用;
//参数举例,其中-和-X 可以通过java命令 java -? 和 java -X 查看和设置
-server -Xss256k -XX:MaxPermSize=512m
Java HotSpot VM中-XX又可分为三类
行为参数(Behavioral Options):用于改变jvm的一些基础行为;
性能调优(Performance Tuning):用于jvm的性能调优;
调试参数(Debugging Options):一般用于打开跟踪、打印、输出等jvm参数,用于显示jvm更加详细的信息;
设置参数
如果是布尔类型的选项,它的格式为-XX:+flag或者-XX:-flag,分别表示开启和关闭该选项。
针对非布尔类型的选项,它的格式为-XX:flag=value
其他参数:
-XX:+PrintGCDetails 打印GC的详细信息
//-Xms5m -Xmx20m -XX:+PrintHeapAtGC
//list.add(new byte[1*1024*1024*4]); 4M
//list.add(new byte[1*1024*1024*10]); 10M
//发生了3次垃圾回收,2次GC回收,1次full GC回收,只有full GC回收后内存仍不足,才会导致内存溢出
//新生区GC回收之前790K,回收之后504K,GC之后新生区堆的总大小3584K,回收之前的堆区4886K,回收之后的堆区4680K,GC之后堆区的总大小16896K,回收耗时0.0016910秒
//user:GC用户耗时,秒;sys:GC系统耗时;rea:GC实际耗时
[GC [PSYoungGen: 790K->504K(3584K)] 4886K->4680K(16896K), 0.0016910 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
//第二次GC,JVM增加了堆的内存(3584K -> 6656K,16896K -> 19968K)
[GC [PSYoungGen: 504K->480K(6656K)] 4680K->4728K(19968K), 0.0011144 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
//PSYoungGen:新生区回收
//ParOldGen:养老区回收
//PSPermGen:永久区回收
[Full GC [PSYoungGen: 480K->0K(6656K)] [ParOldGen: 4248K->492K(5632K)] 4728K->492K(12288K) [PSPermGen: 2577K->2576K(21504K)], 0.0121533 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
//main线程执行完后打印当前堆信息
Heap
PSYoungGen total 6656K, used 307K [0x00000000ff900000, 0x0000000100000000, 0x0000000100000000)
eden space 6144K, 5% used [0x00000000ff900000,0x00000000ff94cdb8,0x00000000fff00000)
from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
to space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
ParOldGen total 13312K, used 10732K [0x00000000fec00000, 0x00000000ff900000, 0x00000000ff900000)
object space 13312K, 80% used [0x00000000fec00000,0x00000000ff67b368,0x00000000ff900000)
PSPermGen total 21504K, used 2583K [0x00000000f9a00000, 0x00000000faf00000, 0x00000000fec00000)
object space 21504K, 12% used [0x00000000f9a00000,0x00000000f9c85ea0,0x00000000faf00000)
-Xloggc:d:/gc.log GC日志路径,不设置则在控制台打印
-XX:+PrintGCTimeStamps GC时,打印时间戳
-XX:+PrintGCDateStamps GC时,打印日期戳
-XX:+PrintHeapAtGC 每次GC后,都打印堆信息,而-XX:+PrintGCDetails是线程结束后打印堆信息
//第一次GC回收前堆信息
{Heap before GC invocations=1 (full 0):
PSYoungGen total 3584K, used 790K [0x00000000ff900000, 0x00000000ffd00000, 0x0000000100000000)
eden space 3072K, 25% used [0x00000000ff900000,0x00000000ff9c5828,0x00000000ffc00000)
from space 512K, 0% used [0x00000000ffc80000,0x00000000ffc80000,0x00000000ffd00000)
to space 512K, 0% used [0x00000000ffc00000,0x00000000ffc00000,0x00000000ffc80000)
ParOldGen total 13312K, used 4096K [0x00000000fec00000, 0x00000000ff900000, 0x00000000ff900000)
object space 13312K, 30% used [0x00000000fec00000,0x00000000ff000010,0x00000000ff900000)
PSPermGen total 21504K, used 2577K [0x00000000f9a00000, 0x00000000faf00000, 0x00000000fec00000)
object space 21504K, 11% used [0x00000000f9a00000,0x00000000f9c846c8,0x00000000faf00000)
//第一次GC回收后堆信息
Heap after GC invocations=1 (full 0):
PSYoungGen total 3584K, used 488K [0x00000000ff900000, 0x0000000100000000, 0x0000000100000000)
eden space 3072K, 0% used [0x00000000ff900000,0x00000000ff900000,0x00000000ffc00000)
from space 512K, 95% used [0x00000000ffc00000,0x00000000ffc7a020,0x00000000ffc80000)
to space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
ParOldGen total 13312K, used 4192K [0x00000000fec00000, 0x00000000ff900000, 0x00000000ff900000)
object space 13312K, 31% used [0x00000000fec00000,0x00000000ff018010,0x00000000ff900000)
PSPermGen total 21504K, used 2577K [0x00000000f9a00000, 0x00000000faf00000, 0x00000000fec00000)
object space 21504K, 11% used [0x00000000f9a00000,0x00000000f9c846c8,0x00000000faf00000)
}
//第二次GC回收前堆信息
{Heap before GC invocations=2 (full 0):
PSYoungGen total 3584K, used 488K [0x00000000ff900000, 0x0000000100000000, 0x0000000100000000)
eden space 3072K, 0% used [0x00000000ff900000,0x00000000ff900000,0x00000000ffc00000)
from space 512K, 95% used [0x00000000ffc00000,0x00000000ffc7a020,0x00000000ffc80000)
to space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
ParOldGen total 13312K, used 4192K [0x00000000fec00000, 0x00000000ff900000, 0x00000000ff900000)
object space 13312K, 31% used [0x00000000fec00000,0x00000000ff018010,0x00000000ff900000)
PSPermGen total 21504K, used 2577K [0x00000000f9a00000, 0x00000000faf00000, 0x00000000fec00000)
object space 21504K, 11% used [0x00000000f9a00000,0x00000000f9c846c8,0x00000000faf00000)
//第二次GC回收后堆信息
Heap after GC invocations=2 (full 0):
PSYoungGen total 6656K, used 504K [0x00000000ff900000, 0x0000000100000000, 0x0000000100000000)
eden space 6144K, 0% used [0x00000000ff900000,0x00000000ff900000,0x00000000fff00000)
from space 512K, 98% used [0x00000000fff80000,0x00000000ffffe030,0x0000000100000000)
to space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
ParOldGen total 13312K, used 4208K [0x00000000fec00000, 0x00000000ff900000, 0x00000000ff900000)
object space 13312K, 31% used [0x00000000fec00000,0x00000000ff01c010,0x00000000ff900000)
PSPermGen total 21504K, used 2577K [0x00000000f9a00000, 0x00000000faf00000, 0x00000000fec00000)
object space 21504K, 11% used [0x00000000f9a00000,0x00000000f9c846c8,0x00000000faf00000)
}
{Heap before GC invocations=3 (full 1):
PSYoungGen total 6656K, used 504K [0x00000000ff900000, 0x0000000100000000, 0x0000000100000000)
eden space 6144K, 0% used [0x00000000ff900000,0x00000000ff900000,0x00000000fff00000)
from space 512K, 98% used [0x00000000fff80000,0x00000000ffffe030,0x0000000100000000)
to space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
ParOldGen total 13312K, used 4208K [0x00000000fec00000, 0x00000000ff900000, 0x00000000ff900000)
object space 13312K, 31% used [0x00000000fec00000,0x00000000ff01c010,0x00000000ff900000)
PSPermGen total 21504K, used 2577K [0x00000000f9a00000, 0x00000000faf00000, 0x00000000fec00000)
object space 21504K, 11% used [0x00000000f9a00000,0x00000000f9c846c8,0x00000000faf00000)
Heap after GC invocations=3 (full 1):
PSYoungGen total 6656K, used 0K [0x00000000ff900000, 0x0000000100000000, 0x0000000100000000)
eden space 6144K, 0% used [0x00000000ff900000,0x00000000ff900000,0x00000000fff00000)
from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
to space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
ParOldGen total 5632K, used 492K [0x00000000fec00000, 0x00000000ff180000, 0x00000000ff900000)
object space 5632K, 8% used [0x00000000fec00000,0x00000000fec7b358,0x00000000ff180000)
PSPermGen total 21504K, used 2576K [0x00000000f9a00000, 0x00000000faf00000, 0x00000000fec00000)
object space 21504K, 11% used [0x00000000f9a00000,0x00000000f9c84340,0x00000000faf00000)
}
-Xms5m -Xmx20m 设置堆大小,-Xms最小值,-Xmx最大值,JVM为最小值而进行GC,如果GC之后扔内存不足,则会增加堆内存,不超过最大值,生产系统为了减少频繁GC,两个值最好相等。
-Xmn 设置新生区大小,默认未设置
-XX:NewRatio 设置新生区与养老区的比值,默认2,表示新生区:养老区=1:2,即新生区站堆总大小的1/3,如果是4,比值就是1:1,1/5。
-XX:SurvivorRatio 设置伊甸区与两个幸存区的比值,默认8,表示两个幸存区:伊甸区=2:8,即一个幸存区栈堆的1/10,如果是2,比值就是2:2,1/4
-Xss256k : 设置每个线程的栈大小。JDK5.0以后每个线程栈大小为1M,以前每个线程堆栈大小为256K。
-XX:+PrintFlagsInitial 查看JVM参数(-XX的参数,例如-XX:NewRatio)启动的初始值
-XX:+PrintFlagsFinal 查看JVM参数(-XX的参数,例如-XX:NewRatio)最终值,java -XX:+PrintFlagsInitial | grep ThreadStackSize 查看线程栈大小,centos默认是1m
JVM 推荐参数
类型 | 参数 |
---|---|
运行模式 | -sever |
整个堆内存大小 | 为-Xms和-Xmx设置相同的值。-Xms2048m -Xmx2048m |
新生代空间大小 | -XX:NewRatio: 2到4. -XX:NewSize=? –XX:MaxNewSize=?. 使用NewSize代替NewRatio也是可以的。设置-Xmn1024m等于同时设置了-XX:NewSize=1024m –XX:MaxNewSize=1024m |
持久代空间大小 | -XX:PermSize=256m -XX:MaxPermSize=256m. 设置一个在运行中不会出现问题的值即可,这个参数不影响性能。 |
线程栈大小 | -Xss256k,如果有大量的递归可以设置为512m或1m,该值越小,线程数就越多,但是不要过小造成栈溢出 |
GC日志 | -Xloggc:$CATALINA_BASE/logs/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps. 记录GC日志并不会特别地影响Java程序性能,推荐你尽可能记录日志。 |
GC算法 | -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75. 一般来说推荐使用这些配置,但是根据程序不同的特性,其他的也有可能更好。注意:当使用Parnew+CMS的时候才这样配 |
发生OOM时创建堆内存转储文件 | -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$CATALINA_BASE/logs |
发生OOM后的操作 | -XX:OnOutOfMemoryError=$CATALINA_HOME/bin/stop.sh 或 -XX:OnOutOfMemoryError=$CATALINA_HOME/bin/restart.sh. 记录内存转储文件后,为了管理的需要执行一个合适的操作。 |
#windows tomcat 设置jvm
#在catalina.bat文件中找到setlocal,在下面设置jvm参数
setlocal
#window下环境变量不区分大小写
set java_opts=-Xms2048m -Xmx2048m
#linux tomcat 设置jvm
#在catalina.sh文件中找到cygwin=false,在上面设置jvm参数
JAVA_OPTS="-Xms2048m -Xmx2048m" #区分大小写
更多参数:https://www.cnblogs.com/milton/p/4380126.html
查看当前java进程的jvm -XX参数
Java代码获取JVM相关参数
package cn.edu.buaa.jvmbook;
import java.lang.management.CompilationMXBean;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.MemoryUsage;
import java.lang.management.OperatingSystemMXBean;
import java.lang.management.RuntimeMXBean;
import java.lang.management.ThreadMXBean;
import java.util.List;
/**
* @author zsm
* @date 2017年1月9日 上午10:37:48
*/
public class GetJVMParameter {
public static void main(String[] args) {
MemoryMXBean memorymbean = ManagementFactory.getMemoryMXBean();
MemoryUsage usage = memorymbean.getHeapMemoryUsage();
System.out.println("INIT HEAP: " + usage.getInit() / 1024 / 1024);
System.out.println("MAX HEAP: " + usage.getMax() / 1024 / 1024);
System.out.println("USE HEAP: " + usage.getUsed() / 1024 / 1024);
System.out.println("\nFull Information:");
System.out.println("Heap Memory Usage: " + memorymbean.getHeapMemoryUsage());
System.out.println("Non-Heap Memory Usage: " + memorymbean.getNonHeapMemoryUsage());
List<String> inputArguments = ManagementFactory.getRuntimeMXBean().getInputArguments();
System.out.println("===================java options=============== ");
System.out.println(inputArguments);
System.out.println("=======================通过java来获取相关系统状态============================ ");
System.out.println("当前堆内存大小totalMemory " + (int) Runtime.getRuntime().totalMemory() / 1024 / 1024 + " M");// 当前堆内存大小
System.out.println("空闲堆内存大小freeMemory " + (int) Runtime.getRuntime().freeMemory() / 1024 / 1024 + " M");// 空闲堆内存大小
System.out.println("最大可用总堆内存maxMemory " + Runtime.getRuntime().maxMemory() / 1024 / 1024 + " M");// 最大可用总堆内存大小
System.out.println("=======================OperatingSystemMXBean============================ ");
OperatingSystemMXBean osm = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
// System.out.println(osm.getFreeSwapSpaceSize()/1024);
// System.out.println(osm.getFreePhysicalMemorySize()/1024);
// System.out.println(osm.getTotalPhysicalMemorySize()/1024);
// 获取操作系统相关信息
System.out.println("osm.getArch() " + osm.getArch());
System.out.println("osm.getAvailableProcessors() " + osm.getAvailableProcessors());
// System.out.println("osm.getCommittedVirtualMemorySize() "+osm.getCommittedVirtualMemorySize());
System.out.println("osm.getName() " + osm.getName());
// System.out.println("osm.getProcessCpuTime() "+osm.getProcessCpuTime());
System.out.println("osm.getVersion() " + osm.getVersion());
// 获取整个虚拟机内存使用情况
System.out.println("=======================MemoryMXBean============================ ");
MemoryMXBean mm = (MemoryMXBean) ManagementFactory.getMemoryMXBean();
System.out.println("getHeapMemoryUsage " + mm.getHeapMemoryUsage());
System.out.println("getNonHeapMemoryUsage " + mm.getNonHeapMemoryUsage());
// 获取各个线程的各种状态,CPU 占用情况,以及整个系统中的线程状况
System.out.println("=======================ThreadMXBean============================ ");
ThreadMXBean tm = (ThreadMXBean) ManagementFactory.getThreadMXBean();
System.out.println("getThreadCount " + tm.getThreadCount());
System.out.println("getPeakThreadCount " + tm.getPeakThreadCount());
System.out.println("getCurrentThreadCpuTime " + tm.getCurrentThreadCpuTime());
System.out.println("getDaemonThreadCount " + tm.getDaemonThreadCount());
System.out.println("getCurrentThreadUserTime " + tm.getCurrentThreadUserTime());
// 当前编译器情况
System.out.println("=======================CompilationMXBean============================ ");
CompilationMXBean gm = (CompilationMXBean) ManagementFactory.getCompilationMXBean();
System.out.println("getName " + gm.getName());
System.out.println("getTotalCompilationTime " + gm.getTotalCompilationTime());
// 获取多个内存池的使用情况
System.out.println("=======================MemoryPoolMXBean============================ ");
List<MemoryPoolMXBean> mpmList = ManagementFactory.getMemoryPoolMXBeans();
for (MemoryPoolMXBean mpm : mpmList) {
System.out.println("getUsage " + mpm.getUsage());
System.out.println("getMemoryManagerNames " + mpm.getMemoryManagerNames().toString());
}
// 获取GC的次数以及花费时间之类的信息
System.out.println("=======================GarbageCollectorMXBean============================ ");
List<GarbageCollectorMXBean> gcmList = ManagementFactory.getGarbageCollectorMXBeans();
for (GarbageCollectorMXBean gcm : gcmList) {
System.out.println("getName " + gcm.getName());
System.out.println("getMemoryPoolNames " + gcm.getMemoryPoolNames());
}
// 获取运行时信息
System.out.println("=======================RuntimeMXBean============================ ");
RuntimeMXBean rmb = (RuntimeMXBean) ManagementFactory.getRuntimeMXBean();
System.out.println("getClassPath " + rmb.getClassPath());
System.out.println("getLibraryPath " + rmb.getLibraryPath());
System.out.println("getVmVersion " + rmb.getVmVersion());
}
}