目录
目录
1. Program Counter Register (程序计数器)
1. Jps [options] [hostid] (hostid 为 ip 或域名地址)
3. jstack 用于生成 java 虚拟机当前时刻的线程快照
1.JVM 简介
1.1. 如何理解 JVM 呢?
1.2. 市场主流 JVM 分析?
1.3. 为什么要学习 JVM?
1.4. 字节码底层是如何执行呢?
JAVA 源程序编译,执行过程如下图所示:
如何理解 JIT 呢?
为什么 JVM 中解释执行与编译执行的并存(混合模式)?
1.5. 如何理解 JVM 的运行模式?
JVM 有两种运行模式 Server 与 Client。两种模式的区别在于,Client 模
如何查看现在 JVM 的工作模式?
2.JVM 体系结构
2.1. JVM 的产品架构是怎样的?
2.2. JVM 运行时内存结构是怎样的?
JVM 启动运行 Class 文件时会对 JVM 内存进行切分,我们可以将其分为线程共
2.永久代
在自定义类加载器还不是很常见的时候,类大多是static的,很少被卸载或收集,因此被成为“永久的(Permanent)”。
同时,由于类class是JVM实现的一部分,并不是由应用创建的,所以又被认为是“非堆(Non-Heap)”内存。
在JDK8之前的HotSpot JVM,存放这些“永久的”区域叫做“永久代(permanent generation)”。
2.2.1. JVM 线程共享区应用分析
1. Heap (堆内存):
2. Method Area (方法区)
2.2.2. JVM 线程私有区应用分析
1. Program Counter Register (程序计数器)
2. Stack Area (虚拟机栈区)
3. Native Stack Area (本地方法栈)
3.JVM 应用参数分析
3.1. 如何理解 JVM 中的内存溢出?
代码演示:堆内存溢出
public class TestOOM01 { public static void main(String[] args) { long t1=System.currentTimeMillis(); try { List<byte[]> list=new ArrayList<>(); for(int i=0;i<100;i++) { list.add(new byte[1024*1024]); } }finally { long t2=System.currentTimeMillis(); //System.out.println("oom:"+(t2-t1)); } } }
运行时可设置虚拟机参数-Xmx50m -Xms10m -XX:+HeapDumpOnOutOfMemoryError-XX:HeapDumpPath=e:/a.dump
代码演示:元数据内存溢出(JDK8)
public class MetaSpaceTest {
public static void main(String[] args) {
int i = 0;
try {
for (i = 0; i < 100000; i++) {
new CglibBean(new HashMap<>());
}
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
System.out.println("total create count:" + i);
}
}
public static class CglibBean {
public CglibBean(Object object) {
Enhancer enhancer = new Enhancer();
enhancer.setUseCache(false);
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> obj);
enhancer.setSuperclass(object.getClass());
enhancer.create();
}
}
}
上述代码通过Cglib生成大量的HashMap代理,下面我们在运行这段代码的时候指定下列参数
-XX:MaxMetaspaceSize=100M -XX:+PrintGCDetails
当我们程序循环至3660次,也就是说我们大约在生成了约3660个代理类以后元数据区发生了内存溢出,下面将MaxMetaspaceSize改为50M执行,
从上图可以看出当我们生成了1710个代理类以后元数据区发生了内存溢出,可见一个元数据区的大小决定了Java虚拟机可以装载的类的多少。
3.2. JVM 工具应用分析篇
3.2.1. 命令行工具篇
1. Jps [options] [hostid] (hostid 为 ip 或域名地址)
2. jmap -heap pid
public class TestCommand {
public static void main(String[] args) {
while (true) {
byte[] b1 = new byte[1024 * 1024];
}
}
}
1) MaxHeapFreeRatio: 最大空闲堆内存比例最大空闲对内存比例, GC 后如果发现空闲堆内存大于整个预估堆内存的 N%(百分比 ) , JVM 则会收缩堆内存,但不能小于 -Xms 指定的最小堆的限制。2) MinHeapFreeRatio: 最小空闲堆内存比例GC 后如果发现空闲堆内存小于整个预估堆内存的 N%( 百分比 ), 则 JVM 会增大 堆内存,但不能超过 -Xmx 指定的最大堆内存限制。3) MaxHeapSize: 即 -Xmx, 堆内存大小的上限4) InitialHeapSize: 即 -Xms, 堆内存大小的初始值5) NewSize: 新生代预估堆内存占用的默认值6) MaxNewSize: 新生代占整个堆内存的最大值7) OldSize: 老年代的默认大小 ,8) NewRatio:老年代对比新生代的空间大小 , 比如 2 代表老年代空间是新生代的两倍大小 .9) SurvivorRatio:Eden/Survivor 的值 . 例如 8 表示 Survivor:Eden=1:8, 因为 survivor 区 有 2 个 , 所以 新生代 Eden 的占比为 8/1010)MetaspaceSize:分配给类元数据空间的初始大小 (Oracle 逻辑存储上的初始高水位,the initial high-water-mark ). 此值为估计值 . MetaspaceSize 设置得过大,会延长垃圾回收时间 . 垃圾回收过后 , 引起下一次垃圾回收的类元数据空间的 大小可能会变大11)MaxMetaspaceSize:是分配给类元数据空间的最大值 , 超过此值就会触发 Full GC. 此值仅受限系统内存的大小 , JVM 会动态地改变此值12)CompressedClassSpaceSize:类指针压缩空间大小 , 默认为 1G.13)G1HeapRegionSize:G1 区块的大小 , 取值为 1M 至 32M. 其取值是要根据最小 Heap 大小划分出 2048 个区块 .说明 :jmap 在系统调优时通常会结合 jhat 来分析 jmap 生成的 dump 文件。
3. jstack 用于生成 java 虚拟机当前时刻的线程快照
class SyncTask02 implements Runnable {
private List<Integer> from;
private List<Integer> to;
private Integer target;
public SyncTask02(List<Integer> from, List<Integer> to, Integer target) {
this.from = from;
this.to = to;
this.target = target;
}
@Override
public void run() {
moveListItem(from, to, target);
}
private static void moveListItem(List<Integer> from,
List<Integer> to, Integer item) {
log("attempting lock for list", from);
synchronized (from) {
log("lock acquired for list", from);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
log("attempting lock for list ", to);
synchronized (to) {
log("lock acquired for list", to);
if (from.remove(item)) {
to.add(item);
}
log("moved item to list ", to);
}
}
}
private static void log(String msg, Object target) {
System.out.println(Thread.currentThread().getName() +
": " + msg + " " +
System.identityHashCode(target));
}
}
public class TestDeadLock02 {
public static void main(String[] args) {
List<Integer> list1 = new ArrayList<>(Arrays.asList(2, 4, 6, 8,
10));
List<Integer> list2 = new ArrayList<>(Arrays.asList(1, 3, 7, 9,
11));
Thread thread1 = new Thread(new SyncTask02(list1, list2, 2));
Thread thread2 = new Thread(new SyncTask02(list2, list1, 9));
thread1.start();
thread2.start();
}
}