Java虚拟内存模型

Java虚拟内存模型_第1张图片
image.png

上图是Java内存模型运行时区域图。

运行时数据区分:

1.程序计数器:

  • 由于java是多线程的,为了能够线程上下切换后能够恢复到正确的折行位置,每条线程都要有一个独立的程序计数器,记录当前线程正在折行的方法的虚拟机指令,该区域没有OOM发生。

2.JAVA虚拟机栈(本地方法栈) - 线程私有的:

  • 生命周期跟线程一样的,描述的是JAVA方法执行的内存模型,每个方法创建调用的时候会创建一个栈帧,由于存储局部变量表等信息。当进入一个方法,栈帧需要的内存是已经确定的。
    会发生Stackoverflow和oom(因为申请栈帧需要内存)

3.JAVA堆:所有线程共享 :

  • 所有对象的实例以及数组都要在堆上分配内存。
    由于收集器基本都是分代收集算法,所以java堆还可以细分为:新生代,老生代,可以处于物理不连续的存储空间。

4.方法区:所有线程共享:

  • 存放虚拟机加载的类的信息,常量,静态变量等。

5.运行时常量池:方法区的一部分,

什么是对象:

  • 对象包括:对象头,实例数据,对其填充。
  • 对象创建:当new时,虚拟机回去检查常量池中是否有对应的类的引用并且是否其被加载,解析和初始化,对象需要的大小在类被加载后就可以确定了
  • java虚拟机维护一个空闲的列表,记录堆上面那个区域是可以用的,
    对象分配好内存后,要将对象对应的类的实例,元数据,对象的哈希码等存放在对象头。

引用

  • 强引用:用 new 出来的
  • 软引用:如果第一次发现内存不够,会将软引用对象放入二次回收中,如果还是内存不足,那就会抛出异常。
  • 弱引用,下一次GC发生的时候,就会回收。
  • 虚引用

回收

  • 当一个对象不可达时,要被回收还要经过2次标记,第一次发现没有对应的GcRoots链后,标记一次,如果发现该对象重写了对应的finalize()方法,那虚拟机会将该对象放在一个F-queue队列里面标记,记住,finalize方法是不能做耗时操作。
    当F-Queue队列被finalize线程执行时,会进行第二次标记,如果这个是finalize方法将该对象重新引用,那么该对象就会被标记为不是回收对象。
    备注:finalize方法只会被虚拟机调用一次。
  1. 回收方法区(永久代):
    方法区回收分为:废弃的常量和无用的类。
  • 判断一个类是无用的:
    1.内存中已经没有该类的任何实例,
    2.加载该类的ClassLoader已经被回收。
    3.该类对应的Class对象没有任何引用。
    (永久代也会溢出的,大量使用反射,动态代理的时候,要适时卸载类)

2.回收算法

  • 标记-清除
  • 重新复制
  • 标记-整理。
目前商用的虚拟机将java堆分为新生代和老生代,然后再根据不同的区域采用不同的回收算法。
一般对象都是分配在eden(新生代),当新生代内存不够时,会发生一次minorGC
  • 需要大对象时,会在老生代分配内存,大对象,比如byte[]数组等
    大对象对于内存分配不友好。更要避免分配临时的大对象,因为有可能在内存还很多的时候,还要触发GC来给大对象分配连续的内存,
    当一个对象经过一段时间的回收后还存活,会将其移动到老生代。

以下况会触发GC,使用的算法是mark-sweep。

  • 其中,Mark阶段从根集(Root Set)开始,递归地标记出当前所有被引用的对象,而Sweep阶段负责回收那些没有被引用的对象
  • gc是虚拟机启动的时候创建的一个线程,每次在堆上面成功创建一个对象的时候,都会检测当前的闲堆大小是否小于等于128k,如果是的话,就会调用有关触发GC_CONCURRENT类型的GC。闲时会等待一定的时间,等待后如果发现没有被调用gc那么它就调用函数trimHeaps对Java堆进行裁剪,以便可以将堆上的一些没有使用到的内存交还给内核。。否则的话,就会调用函数dvmCollectGarbageInternal进行类型为GC_CONCURRENT的GC。
  • 当应用程序调用System.gc、VMRuntime.gc接口,或者接收到SIGUSR1信号时,最终会调用到函数dvmCollectGarbage。

你可能感兴趣的:(Java虚拟内存模型)