JVM内存模型

JVM内存模型其实就是JVM在启动的时候从操作系统内存中要了一块大内存,然后将这个大内存分成五个区域:方法区、堆区、虚拟机栈、本地方法栈、程序计数器。

图例:

java设计一个简易内存池 java 内存池_老年代

方法区

  1. 永久代和元空间
永久代:jdk8之前方法区的具体实现,直接存在堆区
 元空间:jdk8及以后方法区的具体实现,直接内存,os内存

可以这么理解:方法区是一个规范,概念,类似java中的接口。而永久代和元空间是具体的实现类。
类的元信息instanceKlass就是存储在元空间里的

  1. JVM为什么用元空间代替了永久代?
    1.gc算法
    我们知道堆区是存储对象的,而之前把像什么类信息,字段这些存再堆。gc做标记的时候就需要区分开那一块是那一块。而改成元信息之后,gc算法就比较好写了
    2.OOM
    类有时候动态生成,比如像cglib,很容易就OOM了
    3.硬件的发展
    之前是32bit,现在可以有64bit
  2. 元空间最大最小是多少?
最大:  4294901760 =4095M
 最小:	21807104 =20.75M
  1. 元空间如何调优?
    -XX:MetaspaceSize
    -XX:MaxMetaspaceSize
    跟堆一样,最大最小调成一样(可以防止内存抖动)
    调成多少,物理内存的1/32
    visualVm、arthas调试工具

本地方法栈

java调用c、c++的动态链接库,运行里面的函数需要的栈

程序计数器

执行引擎的指针

虚拟机栈


一个线程一个 一个虚拟机栈有多少个栈帧? 方法调用个,一个方法一个

栈帧:

局部变量表

存储局部变量的表(LocalVariableTable)

操作数栈

是使用字节码指令对数据进行操作了,push,pop之类的,需要查看:字节码手册

java设计一个简易内存池 java 内存池_java设计一个简易内存池_02

动态链接
指向方法对象的内存地址
返回地址,保存现场
这一步干了很多事,看这个代码
每个方法都都会生成一个栈帧,对把。我们执行到add()的时候
1. 生成add的栈帧
2. 在add方法的栈帧中保存main方法的字节码的下一行程序计数器
3. 将线程的局部表开始指针(main的)保存到add的栈帧中
4. 将线程的操作数栈开始指针(main的)保存到add的栈帧中
5. 将add的局部表开始指针赋值给线程的局部表指针
6. 将add的操作数栈开始指针赋值给线程的操作数栈的指针

java设计一个简易内存池 java 内存池_堆区_03

堆区

最大大小:1/4实际内存大小
最小大小:1/32实际内存大小

大对象什么时候会进入老年代?
1.15次gc之后,自动进入老年代

为啥是15次?可以调成16次嘛?
	因为它占4bit,对应的二进制等于15.所以只能调小,不能调大

2.大对象
超过eden区的一半,直接进去老年代
eden的大小是动态调整的
3.空间担保
gc以后,Eden区剩下的对象还是比form区或to区都大,又不能丢弃,所以进去老年代
4.动态调整
gc之后,eden区和from区的对象,to区放不下,进入老年代

总结问题


动态链接 2. 虚拟机指向堆区 对象的引用Test obj = new Test() 3. 方法区指向堆区 引用类型的静态属性 4. 堆区指向方法区 Klacc pointer,指向改对象的InstanceKalss实例