01-Jvm内存模型
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 来释放直接内存
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。
评论已关闭