java 内存机制 jvm

java 内存划分

​​在这里插入图片描述


java 内存分为:

  • pc寄存器:用于记录线程当前执行的内存地址 (java 是多线程的 当线程a 失去执行权后 再次获得执行权时 java 需要知道 a线程执行到了哪一步代码 也就是 内存地址)。
  • 本地方法栈:本地方法栈 又叫做 c栈 用于 跟踪native 方法(调用非java 方法)的执行状态和 pc寄存器类似。
  • 栈:线程独有,存储 对象的引用以及类中的局部变量方法参数返回值等。每创建一个线程jvm 就会为这个线程创建一个对应的java 栈 ,在这个java 栈中会包含多个栈帧 这些栈帧 是由多个方法关联起来的,每个一个方法就是一个栈帧 ,顶部的栈帧就是当前执行的方法 ,栈帧 中包含 方法的局部变量 方法的返回值等。
  • 堆:线程共享,是存储java 对象和成员变量的地方,注意子类的会继承父类的非静态属性。
  • 方法区:存储类信息 存储常量信息包括字符串信息。
         方法区是一个规范不是具体的存储位置。
         在jdk8之前的hotspot虚拟机中用永久区实现了方法区(可以理解为方法区的存储位置在永久区中)。
  • 常量池:顾名思义存放常量(常量池 在 方法区中)。
    • 全局常量池:所有类共享,也叫String poll,string pool中存的是引用值而不是具体的实例对象,具体的实例对象是在堆中开辟的一块空间存放的。在HotSpot VM里实现的string pool功能的是一个StringTable类,它是一个哈希表,里面存的是key(字面量“abc”, 即驻留字符串)-value(字符串"abc"实例对象在堆中的引用)键值对,也就是说在堆中的某些字符串实例被这个StringTable引用之后就等同被赋予了”驻留字符串”的身份。这个StringTable在每个HotSpot VM的实例只有一份,被所有的类共享。
    • 静态常量池:每个类独有,这里的常量和我们java语法中的是不一样的这里的常量是指各种字面量(Literal)和符号引用(Symbolic References)。
    • 运行时常量池:jvm加载类时会把静态常量池的内容加载到运行时常量池。

java 在哪些组件中用到内存:

  • java 的堆内存大小在jvm 启动时一次向操作系统申请完成 一旦分配完成不可以改变 (-Xmx 最大值 -Xms 启动时分配值) 堆的内存空间的管理 由 jvm 虚拟机控制 对象创建由java 程序控制 但是对象的空间释放 由 垃圾回收器 完成。

  • jvm 运行实际程序的实体是线程,线程需要内存空间存储必要的数据,每创建一个线程jvm都会给线程分配一个堆栈,堆栈的大小 通常在256kb-756kb(jvm 实现不同大小不同)线程堆栈 所占空间相比堆来说比较小 但是 线程非常多的话 线程堆栈所占空间也会很大 如果线程的数量 比 当前处理器的数据量 多可能导致占用较大的内存和较低的效率

  • java 中的类和类加载器本身也需要存储空间 jdk 存在 堆的永久代中。注意: jvm 是按需加载类 只加载程序中明确使用的类 所以 并不是所有的类都加载到jvm 中 如果要查看加载了那些类 在启动参数加 -verbose:class,通常情况下jvm 只会加载一个类到内存一次 但是如果自己实现类加载器 会出现类重复加载的情况 此时 会导致永久代的内存泄漏 不能对已失效的类进行卸载 通常一个类可以被卸载需要满足以下条件:

    • 在Java堆屮没有对表示该类加载器的java.lang.ClassLoader对象的引用。
    • Java堆没有对表示类加载器加载的类的 任何 java.lang.Class对象的引用。
    • 在Java堆上该类加软器加载的任何类的所有对象都不再存活(被引用)。
      需要注意的是,JVM所创建的3个默认类加载器BootstrapClassLoader、ExtClassLoader和AppClassLoader都不可能满足这些条件,因此,任何系统类(如java.lang.String)或通过应用程序类加载器加载的任何应用程序类都不能在运行时释放。
  • NIO 的缓冲区 用的缓存是 物理机的缓存 直接和内核缓存 交互 而不需要 堆 缓存。

  • JNI 技术使得本机代码(如C讲言程序)可以调用Java方法,也就是通常所说的native memory.实际上Java运行时本身也依赖于JNI 代码来实现类库功能.如文件操作、网络I/O操作或他系统调调用所以JNI也会增加Java运行时的本机内存占用。


内存分配策略:

  • 静态内存分配:在程序编译时已经确定 每个数据的在运行时的存储空间大小 这种编译 形式不允许 代码中有不可知的数据变量 如可变数组 和递归结构。
  • 栈内存分配 也可称为动态内存分配 运行时才知道 所需内存大小,但规定进入一个程序块时必须知道该程序块所需内存大小并为其分配内存,栈内存分配和我们栈数据结构一样 也是先进后出。

jvm 堆结构:

![在这里插入图片描述](https://img-blog.csdn.net/20180615162921895?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI1ODI1OTIz/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70

jvm 将整个堆划分为young 和 old 和perm

  • Young区被划分为一个Eden区和两个Survivor区。Eden区存放新创建的对象,一个Survivor区存放GC后存活的对象,另一个为空。
  • 当Eden区满了,再创建对象,会因为申请不到空间,触发minorGC,进行young区的垃圾回收。 Eden 中存活的和Survivor1中存活的对象放入Survivor2(下一次gc 就是放入Survivor1 所以需要两个)

垃圾回收(GC)过程:

  1. 新生成的对象在Eden区完成内存分配。
  2. 当Eden区满了,再创建对象,会因为申请不到空间,触发minorGC,进行young(eden+1survivor)区的垃圾回收。(为什么是eden+1survivor:两个survivor中始终有一个survivor是空的,空的那个被标记成To Survivor)。
  3. minorGC时,Eden不能被回收的对象被放入到空的survivor(也就是放到To Survivor,同时Eden肯定会被清空),另一个survivor(From Survivor)里不能被GC回收的对象也会被放入这个survivor(To Survivor),始终保证一个survivor是空的。(MinorGC完成之后,To Survivor 和 From Survivor的标记互换)。
  4. 当做第3步的时候,如果发现存放对象的那个survivor满了,则这些对象被copy到old区,或者survivor区没有满,但是有些对象已经足够Old(通过XX:MaxTenuringThreshold参数来设置),也被放入Old区。
  5. 当Old区被放满的之后,进行完整的垃圾回收,即 Full GC。
  6. Full GC时,整理的是Old Generation里的对象,把存活的对象放入到Permanent Generation里 。

垃圾回收算法:

  • 引用计数器法

  对于对象A 任何对象引用了A则A的引用计数器就+1,引用失效时计数器就-1,直到计数器为0时对象A不能再被使用。

  缺点:

  1. 无法循环引用(例如A.b=B;B.a=A 此时循环引用无法回收引起内存泄漏)。
  2. 每次都需要加减操作影响性能。

由于单纯的引用计数器法会有循环引用及性能的问题java 虚拟机并未选择此算法作为垃圾回收算法。

  • 可达性分析算法:

  引用链(GC Roots)的对象作为起点,从这些节点开始向下搜索,搜索走过的路径被称为(Reference Chain),当一个对象到GC Roots没有任何引用链相连时(即从GC Roots节点到该节点不可达),则证明该对象是不可用的。

在这里插入图片描述

  如上图所示,object1-object4对GC Root都是可达的,说明不可被回收,object5和object6对GC Root节点不可达,说明其可以被回收。
  在Java中,可作为GC Root的对象包括以下几种:

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中JNI(即一般说的Native方法)引用的对象
  1. 标记清除算法:

在这里插入图片描述
  分为“标记”和“清除”两个阶段:首先标记出需要回收的对象,标记完成之后统一清除对象。

  缺点:

  • 标记和清除过程效率不高 。

  • 标记清除之后会产生大量不连续的内存碎片。

  1. 复制算法

在这里插入图片描述

  把有用的对象从form 移动到to 然后把 form 中无用的都删除。

  1. 标记整理算法

Å

  标记整理算法的“标记”过程和标记-清除算法一致,只是后面并不是直接对可回收对象进行整理,而是让所有存活的对象都向一段移动,然后直接清理掉端边界意外的内存。

  1. 分代收集算法:

  一般把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对他进行分配担保,就必须使用“标记-整理”算法进行回收。

java类加载机制

在这里插入图片描述

类加载器:

  • BootStrap:老大。类加载器的祖先。 打印它会得到null。负责加载JRE/lib/rt.jar(JDK中绝大部分的类)。
  • ExtClassLoader:负责加载JRE/lib/ext/*.jar。
  • AppClassLoader:负责加载在classpath环境变量中的所有类。加载完后放到一个叫方法区的内存中去。

父委托机制:

  先让父类加载器寻找,只有在父类加载器找不到的情况下才从自己的类路径中去寻找。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值