jvm的内存模型包含什么?

程序计数器(寄存器)

作用:记住下一条jvm执行的执行地址。

程序执行过程中cpu切换任务执行,再次返回是必须知道程序执行到哪一步了。

  • 程序计数器是线程私有的
  • 不存在内存移除

虚拟机栈

栈内存模型后进先出。

虚拟机栈包含了栈帧。每一个线程只能有一个活动栈帧,对应当前正在执行的方法。每一次方法调用都会存在一个栈帧。

栈内存溢出:

  • 栈帧过多(方法调用嵌套太多,不能正常返回。无限递归)
  • 栈帧过大(方法内部维护过多的局部变量)
  • Java.lang.stackOverflowError 栈内存溢出

方法内的局部变量是否存在线程安全问题?

  • 局部变量没逃离方法的作用访问,就是线程安全额
  • 局部变量引用了对象,逃离方法作用范围,可能是线程不安全的

虚拟机栈不涉及垃圾回收,因为方法调用由栈帧组成,执行方法完毕后,栈帧弹出,无需回收。

本地方法栈

native关键字修饰的方法,有c、c++编写。称为本地方法

通过new关键字创建的对象,都存在堆内存中。

  • 线程共享,需要考虑线程安全问题(java.lang.OutofMemoryError :java heap space. 堆内存溢出)
  • 存在垃圾回收机制

堆内存诊断工具:

  • jps:查看当前新系统的java进程
  • jmap:查看堆内存占用情况 jmap -heap 进程id
  • jconsole:图形化界面,可以检测很多jvm参数,内存,线程,死锁等。

方法区

存储每类结构,如运行时间常数池、字段和方法数据,以及方法和构造器的代码,包括类和实例初始化和接口初始化中使用的特殊方法。方法区创建在虚拟机器启动时。

方法区存在内存移除问题:

  • jdk1.8以前方法区实现为永久代,Java.lang.OutOfMemoryError: PermGen space
  • jdk1.8以后是元空间实现,Java.lang.OutOfMemoryError: Metaspace。设置大小的参数:

    -XX:MaxMetaspaceSize=8m

常量池

常量池,就是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量 等信息。

运行时常量池

常量池是 *.class 文件中的,当该类被加载,它的常量池信息就会放入运行时常量 池,并把里面的符号地址变为真实地址。

StringTable

位置:

  • 1.6存储在永久代中。是常量池的一部分
  • 1.7之后转移到了堆内存中(原因:永久代回收效率有点低,full gc触发。触发条件是老年代空间不足。minor gc 堆内存回收效率高,减少字符串常量对内存的占用。

StringTable性能调优:

串池的实现是StringTable是hashtable实现的。

  • 增加串池的大小(增加hashtable桶的个数)可以减少节点的大小,减少字符串放入串池的时间

    jvm参数调整串池大小:-XX:StringTableSize=xxxx,最小为1009

  • 将字符串放入串池,可以减少jvm中字符串常量的个数,减少内存占用

StringTable垃圾回收:

  • StringTable在内存紧张时,会发生垃圾回收
intern()
  • 1.6 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有会把此对象复制一份, 放入串池, 会把串池中的对象返回
  • jdk1.8,intern()主动将字符串放入串池中,如果串池中已经存在,不会放入,如果没有则会放入串池。会把串池中的对象返回
/**
     * 字符串拼接:如果是常量拼接是在编译器优化的:String str = "a" + "b";
     *          如果是变量拼接1.8使用StringBuilder拼接toString()。String str = a + b;
     * jdk1.8,intern()主动将字符串放入串池中,如果串池中已经存在,不会放入,如果没有则会放入串池。会把串池中的对象返回
     * jdk1.6,intern() 如果不存在会拷贝一份对象放入串池
     * @param args
     */
public static void method1(){
    String s1 = "a";
    String s2 = "b";
    String s3 = "a"+"b"; //编译时优化“ab”
    String s4 = s1+s2;//变量拼接 new String(“ab”);
    String s5 = "ab";//串池对象
    String s6 = s4.intern();//串池对象

    System.out.println(s3 == s4);//false
    System.out.println(s3 == s5); //true
    System.out.println(s3 == s6);//true
}

直接内存

直接内存是操作系统和Java代码都可以访问的一块区域,无需将代码从系统内存复制到Java堆内存,从而提高了效率

  • 见于 NIO 操作时,用于数据缓冲区
  • 分配回收成本较高,但读写性能高
  • 不受 JVM 内存回收管理

分配回收原理:

  • 使用了 Unsafe 对象完成直接内存的分配回收,并且回收需要主动调用 freeMemory 方法
  • ByteBuffer 的实现类内部,使用了 Cleaner (虚引用)来监测 ByteBuffer 对象,一旦 ByteBuffer 对象被垃圾回收,那么就会由 ReferenceHandler 线程通过 Cleaner 的 clean 方法调 用 freeMemory 来释放直接内存
文章目录