程序计数器 数据线程独占区每一个线程都有一个单独的程序计数器。此区域是唯一一个在java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。
虚拟机栈 描述的是java方法执行的动态内存模型
栈帧:虚拟机每个线程都有一个固定的大小由参数Xss分配。每个方法执行都会创建一个栈帧伴随着方法从创建到执行完成 用于存储变量表,操作数栈,动态链接每个方法从调用直至执行完成的过程就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。 当栈帧满了之后如果依旧会有栈继续进入则有StackOverFlowError异常如果这时未给栈帧设置大小则OutOfMemory,如递归调用
局部变量表
虚拟机堆 大部分java对象实例
方法区 线程共享的情况下虚拟机加载的类信息,常量,静态变量即编译器编译后的代码等数据
运行时常量池
对象的创建
new 类名 --> 根据new的参数在常量池中定位一个类的符号引用 --> 如果没有找到这个符号引用说明类还没有被加载则进行类的加载解析和初始化 --> 虚拟机为对象分配内存(堆中) --> 将分配的内存初始化为零值 --> 调用对象的init方法
优先分配到eden
大对象直接分配到老年代
长期存活的对象分配到老年代
空间分配担保
动态对象年龄判断
在运行参数(idea -- VM options)中设置 -verbose:gc -XX:+PrintGCDetails ( -XX:+UseSerialGC ) 并选择使用指定的GC的收集器控制台打印内存分配的信息如下:
Heap
PSYoungGen total 38400K, used 9429K [0x00000000d5f80000, 0x00000000d8a00000, 0x0000000100000000)
eden space 33280K, 28% used [0x00000000d5f80000,0x00000000d68b56b0,0x00000000d8000000)
from space 5120K, 0% used [0x00000000d8500000,0x00000000d8500000,0x00000000d8a00000)
to space 5120K, 0% used [0x00000000d8000000,0x00000000d8000000,0x00000000d8500000)
ParOldGen total 87552K, used 0K [0x0000000081e00000, 0x0000000087380000, 0x00000000d5f80000)
object space 87552K, 0% used [0x0000000081e00000,0x0000000081e00000,0x0000000087380000)
Metaspace used 3388K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 371K, capacity 388K, committed 512K, reserved 1048576K
从而可以看出在一定的标准下优先分配到eden区域。
现在设置eden区域的大小为8M: -Xmx20M -Xmn10M -XX:SurvivorRatio=8M。当存放如下内存时
byte[] b1 = new byte[2 * 1024 * 1024];
byte[] b2 = new byte[2 * 1024 * 1024];
byte[] b3 = new byte[2 * 1024 * 1024];
byte[] b4 = new byte[4 * 1024 * 1024];
[GC (Allocation Failure) [DefNew: 7986K->1023K(9216K), 0.0036998 secs] 7986K->5254K(19456K), 0.0037303 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [Tenured: 4230K->6278K(10240K), 0.0034140 secs] 11638K->11362K(19456K), [Metaspace: 3420K->3420K(1056768K)], 0.0034400 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
Heap
def new generation total 9216K, used 5385K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
eden space 8192K, 65% used [0x00000000fec00000, 0x00000000ff142500, 0x00000000ff400000)
from space 1024K, 0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
to space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
tenured generation total 10240K, used 6278K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
the space 10240K, 61% used [0x00000000ff600000, 0x00000000ffc21930, 0x00000000ffc21a00, 0x0000000100000000)
Metaspace used 3441K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 372K, capacity 388K, committed 512K, reserved 1048576K
在为新生代分配10M内存eden区域分配8M之后,b1b2b3进入eden还剩余2M故b4无法进入只能向老年代借用内存发生第一次GC。这次GC发生在新生代时间较短相对也较为频繁。当b4再次进入时再次分配到eden区域。
大对象直接进入老年代 -XX:PretenureSizeThreshold 设置大对象的大小
空间分配担保 -XX:+HandlerPromotionFailure 满足eden是否能存放下并且开启了此参数(+ / -)
逃逸分析与栈上分配 分析对象的作用域只有发生在方法体内部才能认为没有发生逃逸,没有发生逃逸则资源直接分配到栈上。无论是存在返回值还是在方法体内引用了成员变量都认为存在逃逸。如下几种情况都认为是发生了逃逸
static V global_v;
public void a_method(){
V v=b_method();
c_method();
}
public V b_method(){
V v=new V();
return v;
}
public void c_method(){
global_v=new V();
}
虚拟机工具
jstat
堆内存 = 年轻代 + 老年代 + 永久代 ; 年轻代 = eden区+两个Survivor区
jinfo 实时查看和调整虚拟机的各项参数
jmap jmap -dump:format=b,file=d:\a.bin 2345下载dump文件为.bin文件。
jstack 用于生成当前时刻的线程快照,线程快照指的是当前java虚拟机内每一条线程正在执行的方法堆栈的集合生成快照的主要目的是定位线程出现长时间停顿的原因如线程间死锁,死循环,请求外部资源导致的长时间等待等。所以指定线程名字监控线程信息会对线程定位会有很大的帮助。
RUNNING 运行状态
BLOCKED 受阻塞并等待监视器锁
WATING 无限等待
TIMED_WAITING 有时限的等待另一个线程的特定操作
jstack pid 查看当前pid运行情况
1.top 查找出哪个进程消耗的cpu高。执行top命令,默认是进程视图,其中PID是进程号
co_ad2 18 0 1817m 776m 9712 S 3.3 4.9 12:03.24 java
co_ad 21 0 3028m 2.5g 9432 S 1.0 16.3 6629:44 ja
这里我们分析21125这个java进程
2.top -H p 进程ID 查看当前进程中使用线程CPU最高的线程
co_ad2 15 0 1807m 630m 9492 S 1.3 4.0 0:05.12 java
co_ad2_s 15 0 1360m 560m 9176 S 0.3 3.6 0:46.72 java
这里我们分析21233这个线程,并且注意的是,这个线程是属于21125这个进程的。
3.打印这一时刻的堆栈信息 sudo -u admin /usr/home/java/bin/jstack -l pid > /home/log/jstack.log
由于jstack.log文件记录的线程ID是16进制,需要将top命令展示的线程号转换为16进制。
4. jstack查找这个线程的信息
jstack [进程]|grep -A 10 [线程的16进制]
即: jstack 21125|grep -A 10 52f1
-A 10表示查找到所在行的后10行。21233用计算器转换为16进制52f1,注意字母是小写。
结果:
"http-8081-11" daemon prio=10 tid=0x00002aab049a1800 nid=0x52bb in Object.wait() [0x0000000042c75000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:485)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.await(JIoEndpoint.java:416)
在结果中查找52f1,可看到当前线程在做什么。