当前位置 : 主页 > 网络编程 > 其它编程 >

java内存机制jvm

来源:互联网 收集:自由互联 发布时间:2023-07-02
java内存划分​​java内存分为:pc寄存器用于记录线程当前执行的内存地址(java是多线程的当线程a失去执行权后再次获得执行权时java需要知道a线程执行 java 内存划分 ​​ java 内存分为:
java内存划分​​java内存分为:pc寄存器用于记录线程当前执行的内存地址(java是多线程的当线程a失去执行权后再次获得执行权时java需要知道a线程执行

java 内存划分

​​在这里插入图片描述


java 内存分为:

  • pc寄存器用于记录线程当前执行的内存地址 (java 是多线程的 当线程a 失去执行权后 再次获得执行权时 java 需要知道 a线程执行到了哪一步代码 也就是 内存地址)。
  • 本地方法栈本地方法栈 又叫做 c栈 用于 跟踪native 方法(调用非java 方法)的执行状态和 pc寄存器类似。
  • 栈线程独有存储 对象的引用以及类中的局部变量方法参数返回值等。每创建一个线程jvm 就会为这个线程创建一个对应的java 栈 在这个java 栈中会包含多个栈帧 这些栈帧 是由多个方法关联起来的,每个一个方法就是一个栈帧 ,顶部的栈帧就是当前执行的方法 ,栈帧 中包含 方法的局部变量 方法的返回值等。
  • 堆线程共享是存储java 对象和成员变量的地方注意子类的会继承父类的非静态属性。
  • 方法区存储类信息 存储常量信息包括字符串信息。      方法区是一个规范不是具体的存储位置。      在jdk8之前的hotspot虚拟机中用永久区实现了方法区(可以理解为方法区的存储位置在永久区中)。
  • 常量池顾名思义存放常量(常量池 在 方法区中)。
    • 全局常量池所有类共享也叫String pollstring 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)过程

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

    • 引用计数器法

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

      缺点:

  • 无法循环引用(例如A.bB;B.aA 此时循环引用无法回收引起内存泄漏)。
  • 每次都需要加减操作影响性能。
  • 由于单纯的引用计数器法会有循环引用及性能的问题java 虚拟机并未选择此算法作为垃圾回收算法。

    • 可达性分析算法

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

    在这里插入图片描述

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

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

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

      缺点

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

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

  • 复制算法

  • 在这里插入图片描述

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

  • 标记整理算法

  • Å

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

  • 分代收集算法

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

    java类加载机制

    在这里插入图片描述

    类加载器:

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

    父委托机制

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

    上一篇:vi的命令真是sexy到不行
    下一篇:没有了
    网友评论