jvm——自动内存管理机制

1、java技术体系:
(1)java程序设计语言
(2)java虚拟机
(3)class文件格式
(4)java API类库
(5)第三方java类库
jdk:(1)、(2)、(4)
jre:(2)(4)
按照业务:JME、JSE、JEE
2、虚拟机内存管理
这里写图片描述
程序计数器: (线程私有)
(1)指示当前线程所执行字节码的行号,用于取字节码的下一条指令。分支、循环、跳转、异常、线程恢复有依赖与计数器。
(2)java多线程中,为了线程切换后能恢复到正确位置,每个线程都有独立的程序计数器。
(3)如果是java方法,计数器记录的是正在执行的字节码的地址,如果是native方法(与平台有关)则计数器的值为空。
java虚拟机栈:(线程私有)
(1)描述java方法执行的内存模型,每个方法执行时会创建一个栈帧,用于存储局部变量表(存放基本数据类型、地址)、操作栈、动态链接、方法出口等信息。方法被调用到执行完成对应栈帧在虚拟机栈中从入栈到出栈。
如果线程请求的栈深度大于虚拟机所允许的深度会抛出StackOverflowError异常。
若虚拟机栈动态扩展也无法申请到足够内存会抛出OutOfMemoryError异常。
本地方法栈:
(1)与虚拟机栈类似,本地方法栈为native方法服务。可能会抛出StackOverflowError、OutOfMemoryError
java堆(线程共享)
(1)占用内存最大,被所有线程共享,虚拟机启动时创建。几乎所有对象都在这里分配内存。
(2)垃圾收集器所管理的主要区域。
(3)如果堆内存不足以完成实例分配,并且也无法再扩展。会抛出OutOfMemoryError异常。
方法区(线程共享)(NonHeap)
(1)存储的是类信息、常量、静态变量、编译后的代码。
(2)若方法区无法满足内存分配需要,会抛出OutOfMemoryError异常。
(3)运行常量池是方法区的一部分。class文件包括:类版本、字段、方法、接口、常量池(编译生成的各种字面量、符号引用)。类加载后,常量池会加载到运行常量池中。若常量池无法申请到内存,会抛出OutOfMemoryError异常。

直接内存:不在虚拟机管理的内存中,由Native函数库直接分配,由java堆中的DirectByteBuffer对象作为这块内存的引用。会受到本机物理内存的限制,抛出OutOfMemoryError异常。
java 8 中,以Metaspace(元空间)代替堆中的Permanent Generation。这部分数据在本地内存中分配,受可用的本地内存限制
3、java数据类型:
基本类型( primitive):整型、浮点型、布尔、returnAddress
引用类型( reference):类类型、接口类型、数组类型
reference 有四种:
(1)Strong Reference:类似 Object obj = new Object() ,只要引用还在,GC不会回被引用的对象。
(2)Soft Reference:描述一些还有用,并非必需的对象。在系统将要发生内存溢出前,GC会把这些对象回收。
(3)Weak Reference:描述非必需的对象,强度要弱于Soft Reference。无论内存是否足够,GC直接回收。
(4)Phantom Reference:最弱的引用,这种对象被回收时,会收到系统通知。
4、对象访问:
Object obj=new Object();
Object obj : 分配到java栈中的变量表,为reference类型。
new Object :分配到java堆中。
引用如何访问对象?
两种方式:使用句柄、直接指针
(1)使用句柄:在堆中划分出一块内存作为句柄池,reference类型存储的是对象的句柄地址。句柄包括对象数据。(对象被移动后,无需改变reference)
(2)直接指针:reference存储的是对象地址。(访问速度快)
sun HotSpot使用的是直接指针。
5、垃圾收集器:(Garbage Collection)
内存分配与回收:
(1)程序计数器、虚拟机栈、本地方法栈 由每个线程私有,在方法结束或线程结束时释放内存。
(2)垃圾收集器回收的是java堆、方法区内存。
判断对象是否存活:
(1)引用计数
(2)根搜索算法(跟踪收集器):将名为GC Roots的对象作为起始点开始搜索其他对象,搜索走过的路径称为引用链,若一个对象到GC Roots没有任何引用链(无法到达的对象),就认为该对象不可用,可以被回收。
GC Roots对象种类:虚拟机栈中引用的对象、方法区中的常量或类的静态变量引用的对象、本地方法栈中JNI(Native方法)引用的对象
在根搜索算法中,不可到达的对象也不一定会被回收。不可到达的对象会被标记,然后对这个对象进行判断是否要执行finalize() 方法:
判断条件:若该对象没有覆盖finalize()方法,或finalize()方法被jvm执行过,则认为没有必要执行finalize()方法。
若对象被判定为有必要执行finalize()方法,则会被放到F-Queue队列中,然后GC会对其进行第二次标记。若这个对象在finalize()方法中能够与引用链上的对象建立关联(将this赋值给类变量或对象的成员变量),则就不会被回收。
例如:

package com.hi;

public class myjava {

    public static myjava save=null;
    public void isAlive(){
        System.out.println("yes,alive");
    }
    @Override
    protected void finalize() throws Throwable {
        // TODO Auto-generated method stub
        super.finalize();
        System.out.println("finalize method executed!");
        myjava.save=this;
    }

    public static void main(String[] args) throws Exception{
      save=new myjava();

      save=null;  //save对象被标记
      System.gc();  //因为finalize()方法被覆盖,有必要执行finalize()方法。
      Thread.sleep(500);
      if(save!=null){
          save.isAlive();//由于在finalize()方法中与引用链上的类变量相关联,故GC不会回收save对象。
      }
      else{
          System.out.println("dead");
      }

      save=null;
      System.gc();
      Thread.sleep(500);
      if(save!=null){
          save.isAlive();
      }
      else{
          System.out.println("dead");//finalize()方法只能被系统执行一次
      } 
      }
}
//输出:
//finalize method executed!
//yes,alive
//dead

方法区内存回收:
常量被回收的条件:常量没有被引用
类被回收的条件:这个类的所有实例被回收、这个类的ClassLoader被回收、这个类对应的java.lang.Class对象没有被引用,无法通过反射获取这个类的信息。
垃圾收集算法:
(1)标记-清除算法:(效率低、产生内存碎片)
先标记需要回收的对象,然后回收标记的对象。
(2)复制算法:(商业jvm采用的算法)(若对象存活率较高,会执行较多的复制操作,降低效率)
将可用内存划分成大小相等的两个区域,每次使用其中一个,当这一块内存用完时,将这块内存上还存活的对象复制到另一块上,然后把这块内存清空。
将堆内存的新生代划分为3块:Eden、Survivor0、Survivor1
每次使用Eden和其中的一个Survivor,当回收时,将Eden和Survivor还存活的对象拷贝到另外一个Survivor中,然后清空Eden和Survivor。
HopSpot默认的 Eden:Survivor=8:1
(3)标记-整理算法:
先标记要回收的对象,然后将存活的对象移动到一端,清理端边界以外的对象。
(4)分代收集算法:
根据对象的存活周期的不同将内存划分为几块。新生代(复制算法)、老年代(标记-整理、标记-清除)
垃圾收集器:
HotSpot
(HotSpot JVM1.6 垃圾收集器)
连线的收集器可以协同工作。
(1)Serial 收集器:单线程收集器(一个线程收集),运行时会暂停用户的工作线程。简单高效。
(2)ParNew收集器:Serial收集器的多线程版本。(多个线程收集),运行时会暂停用户的工作线程。
(3)Parallel Scavenge 收集器:多个线程收集,运行时会暂停用户的工作线程。可以控制吞吐量(用户代码时间/(用户代码时间+垃圾收集时间)),吞吐量越高,用户时间越长。自适应调节吞吐量。
(4)Serial Old : Serial 的老年代版本。
(5)Parallel Old :Parallel Scavenge 收集器的老年代版本。
(6)CMS(concurrent mark sweep):4个步骤:
初始标记、并发标记、重新标记、并发清除。
并发标记、并发清除可以和用户线程同时进行。
并发收集、低停顿。对cpu资源敏感、采用标记-清除算法,易产生碎片。
(7)G1 (garbage first):标记-整理算法、可以控制垃圾回收停顿时间。

版权声明:本文为misterfm原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/misterfm/article/details/80566673