java内存学习笔记

    技术2024-03-10  76

    目录

    JVM内存堆和栈方法区 GC指针压缩

    JVM内存

    jvm将自己拿到的内存分成五个部分:栈,堆,程序计数器,本地方法站,方法区。

    本地方法栈: C++的native方法运行时候的栈栈: 函数运行过程中的临时变量,栈存储的对象实际是引用类型,存储的是一个地址,最终是指向堆区。堆:主要存对象。程序计数器:指向程序当前运行的位置方法区:存取一些元数据,JDK7之前叫做永久带,JDK8之后改名为元数据空间,主要存储一些静态方法或变量(static), 类加载器(class loader)

    栈+本地方法栈+程序计数器= 线程私有,每个线程会单独创建这样一份内存 。

    堆和方法区是全局共享。

    程序运行结束之后,会将创建的栈空间 整个删除。

    堆和栈

    值创建过程

    如上图,首先main线程创建一个栈,保存A,之后func1函数会创建新的栈,运行结束,删除栈。

    对象创建过程: 只看fun1函数,首先为变量a,b栈上申请空间,new一个对象会发生,在堆上申请一块内存,包含两个部分,一个是地址,一个是内容,默认id = 0, name = null. 堆保存一个地址指向栈中的地址。下一行id 进行赋值,下一行name 进行赋值发现String也是一个对象,在堆中 新申请一块内存,保存char数组的值内存地址,person对象中name地段保存String的地址。

    运行完毕之后,栈空间删除,但是堆上的空间怎么处理呢?

    GC机制,垃圾回收。

    方法区

    保存static 变量或者方法, 全局。

    GC

    如何判断对象是否需要被清楚

    GCroot

    被栈引用或者间接引用;

    或者被本地方法栈引用或间接引用的对象;

    或者被方法区直接或者间接引用的这些对象;

    这些对象是不可以被删除。

    清理堆区对象的思路

    标记需要删除的对象—标记清理

    缺点:产生内存碎片

    标记整理 – 删除之后整理,比如删掉第一个后面的往前顶。

    缺点:代价开销大

    复制算法

    将整个内存一分为二, 将对象创建在一区,然后需要删除的时候标记删除对象,之后不是直接删除,而是向二区复制不需要删除的对象。

    缺点:需要两倍的内存。

    实际的做法

    划分堆- 年轻代(young), 老年代(0ld)

    young再次划分三块:E, S, S (为表区分,S0, S1)

    new对象出生在E区,E区快满之后,触发GC,也叫作young GC, 使用复制算法,复制到S0区。

    对象朝生夕死,产生的比较大,但是幸存下来比较小,所以E区较大,S区较小。

    为什么需要两个S区,

    这两个S区交替工作,比如E幸存到S0区,删除E区和S1区,下一次E区快慢之后,标记S0和E区,将所有的幸存者保存到S1区,清空另外两个区。如此反复。

    OLD区: 每一次youngGC,年龄就会加一,如果每一次Young GC都活下来了,如果满6岁,不在向S中复制,直接放在OLD区中维护,省的每次都在young区复制。

    OLD保存六岁以上的对象还有一些大对象

    大对象的消耗比较大,比如一个100000000大小的int数组。

    OLD的内存快满也会触发GC, 每次OLD GC一般也会引起young GC,这也叫full GC, 引起Stop the world, java程序直接暂停,全力进行垃圾回收, 主要使用 标记清理或者标记整理算法。

    垃圾收集器:ParNew, CMS, G1,其中G1有全新的理念。

    指针压缩

    对象的组成:对象的头和body

    头也分成三个部分:

    mark world(8B) ,记录对象信息,是否锁住,GC年龄等

    Klass world(4B), 指向C++中对象的地址,Klass指针指向metaspace区的对象, Klass对象记录对象的元数据信息。

    Array length(4B),对象不是数组类型不存在这个字段

    栈中保存值类型,也保存对象引用,如果指针压缩就是4字节,没有压缩就是8字节.

    为什么对象的指针可以是4字节

    因为java对象是8字节对齐,每个地址住8个字节而不是1个字节,所以4字节,可以表示2^32 * 8 Byte= 32GB的内存地址。

    堆内存在32G以内都是默认开启指针压缩,每个对象的地址用4字节宝石,但是堆超过32G无法压缩每个对象地址必须要用8字节。

    可能33G, 34G堆大小中申请的对象的个数反而没有32位中申请的多。 地址变大了,变成8个字节了。

    普通对象指针压缩技术: 四字节地址可以对32G内存寻址。

    klass C++对象并不是普通对象,为什么也可以压缩实现呢?

    在元空间中,开启压缩后,KLASS部分位于压缩累空间上,包括ITABLE, VTABLE,压缩类空间是连续的4GB的空间,这里的32bit确实是表示4GB的空间。

    避免创建巨大的动态代理链,会占用metaspace,撑爆压缩累空间,程序报错。

    metaSpace开启指针压缩之后,分成两个部分:

    压缩类空间:一些KLASS, itable等较小的数据

    non-Class: 除上面之间的元数据信息;


    原视频连接 视频连接2

    Processed: 0.012, SQL: 9