1、程序计数器

PC

作用:

  • 记住下一条的jvm指令的执行地址

特点:

  • 是线程私有的
  • 不会存在内存溢出

2、栈

线程运行空间

栈帧:方法运行空间

内存结构 - 图1

  • 每个线程只有一个活动栈帧,对应着当前正在执行的那个方法。

问题辨析:

1、垃圾回收是否涉及栈内存
不会,因为线程调用自动释放,不需要回收
2、栈内存分配越大越好吗 :::tips -Xss sizes ::: 不是,会影响可并行的线程数
3、方法内的局部变量是否是线程安全的

  • 如果方法内部的局部变量没有脱离方法的作用范围,它是线程安全的
  • 如果局部变量引用了对象,并逃离方法的作用范围,那么需要讨论线程安全问题。

栈内存溢出

  • 递归调用(StackOverFlow)
  • 栈帧过大

是安全的,因为变量存储在每个独立的线程中

CPU问题诊断

案例一:CPU占用过高
定位:

  • 用top定位哪个进程对cpu的占用过高
  • ps H -eo pid,tid,%cpu | grep 进程id 定义线程
  • jstack 进程id
    • 可以根据线程id找到问题的id(16进制),并找到运行的行数

案例2:程序运行时间过长
定位:

  • 直接用jstack查看是否死锁

3、本地方法栈

Native Method Stack

4、堆

4.1、定义

Heap:

  • 通过new关键字,创建对象都会使用堆内存

特点:

  • 线程共享的,堆中对象都需要考虑线程安全的问题
  • 有垃圾回收机制

4.2、堆内存溢出

outOfMemory

Xmx

4.3、堆内存诊断

  1. jps工具
  • 查看当前系统中有哪些java进程
  1. jmap工具
  • 查看堆内存占用情况
  • jmap -heap 进程id
  1. jconsole工具
  • 图形界面,多功能的检测工具,可以连续监测

4.3、问题排查

1、内存过高

5、方法区

The Java Virtual Machine has a method area that is shared among all Java VirtuaMachine threads. The method area is analogous to the storage area for compiled codeof a conventional language or analogous to the “text” segment in an operating systemprocess. lt stores per-class structures such as the run-time constant pool, field andmethod data, and the code for methods and constructors, including the specialmethods (S29) used in class and instance initialization and interface initialization. The method area is created on virtual machine start-upAlthough the method area isogically part of the heap, simple implementations may choose not to either garbagecollect or compact it, This specification does not mandate the location of the methocarea or the policies used to manage compiled code. The method area mav be of afixed size or mav be expanded as required by the computation and may be contractecif a larger method area becomes unnecessary. The memory for the method area doesnot need to be contiquous。 A Java Virtual Machine implementation may provide the programmer or the user control over theinitial size of the method area, as well as, in the case of a varying-size method area, control over themaximum and minimum method area size。 The following exceptional condition is associated with the method area: lf memory in the method area cannot be made available to satisfy anallocation request, the Java Virtual Machine throws an outofMemoryError。

内存结构 - 图2

5.1、方法区内存溢出:

  1. package com.tao;
  2. import jdk.internal.org.objectweb.asm.ClassWriter;
  3. import jdk.internal.org.objectweb.asm.Opcodes;
  4. //--XX:MaxMetaspaceSize=8m
  5. public class Demo_8 extends ClassLoader { //用来加载类的二进制字节码
  6. public static void main(String[] args) {
  7. int j=0;
  8. try{
  9. Demo_8 demo_8 = new Demo_8();
  10. for (int i = 0; i < 100000; i++,j++) {
  11. ClassWriter cw = new ClassWriter(0);//用来生成类的二进制字节码
  12. //参数1,版本号 参数2:public,类名,包名,父类,接口
  13. cw.visit(Opcodes.V1_8,Opcodes.ACC_PUBLIC,"Class"+i,null,"java/lang/Object",null);
  14. //返回一个byte数组
  15. byte[] code = cw.toByteArray();
  16. //执行力类的加载
  17. demo_8.defineClass("Class"+i,code,0,code.length);
  18. }
  19. }finally {
  20. System.out.println(j);
  21. }
  22. }
  23. }

同样的代码虽然在内存溢出时都会报错

  • 1.6:PermGen Space
  • 1.8: MetaSpace

5.2、场景

  • Spring
  • mybatis

5.3、运行时常量池

  • 常量池,就是一张表,虚拟机指令根据这张常量表要找到执行的类名,方法名,参数类型,字面量等信息。
  • 运行时常量池:常量池时*.class 文件中的,当类被加载。它的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实地址。

    5.4、StringTable 特性

  • 常量池中的字符串仅是符号,第一次使用时才会变为对象

  • 利用串池的机制,避免重复创建对象
  • 字符串变量的拼接原理(StringBuilder)
  • 字符串常量拼接原理是编译期优化
  • 可以使用intern 方法,主动将串池中还没有的字符串对象放入串池 ```java package jvm; public class T2 { //StringTable [“a”,”b”,”ab”] hashtable 不能扩容 public static void main(String[] args) {

    1. String s1 = "a";
    2. String s2 = "b";
    3. String s3 = "ab";
    4. String s4 = s1+s2; //new StringBuilder().append("a").append("b").toString
    5. System.out.println(s3==s4); //false 一个是常量池,一个是对象
    6. String s5="a"+"b"; //直接引用常量池的 s3,编译期的优化,结果已经在编译期确定为ab
    7. System.out.println(s3==s5); //true
    8. String s6 = new String("a")+new String("b");
    9. //此时s6 是创建的对象会放入堆中
    10. String s7 = s6.intern(); //如果常量池中不存在则放入,否则不放入,最后会把常量池的对象返回
    11. System.out.println(s7 == s6); //false

    } }

  1. > 直接执行下列代码,结果都是true,这是因为常量池无“ab”,所以返回值和原来的对象都是常量池中的值
  2. ```java
  3. String s6 = new String("a")+new String("b");
  4. //此时s6 是创建的对象会放入堆中
  5. String s7 = s6.intern(); //如果常量池中不存在则放入,否则不放入,最后会把常量池的对象返回
  6. System.out.println(s7 == s6); // true
  7. System.out.println(s7 == "ab"); //true

在1.6 中,intern方法执行时,如果常量池没有元素,则会把对象先复制一份,再返回常量池中的值,原来的对象还是存放在堆中。