020-29815005
预约专线时间:09:00-23:59

对回收机制的详细理解是Java的基础

新闻来源:本站 日期:2021-06-01
内存管理一直是C/C++开发中需要慎重考虑的一个重要方面。
  对于C语言,我们使用库函数malloc()和free()两个库函数来实现从堆中分配内存和释放内存,而C++使用操作符new和delete来管理内存,对于这两种方法,后者是操作符,而前者是库函数,可以由编译器处理,前者侧重于内部数据实现构造,在面向对象的设计中,后者可以更好地将构造函数与自定义对象相结合实现内存分配。但在接触Java后,我们可以很容易地管理内存,关键在于Java实现了内存的自动管理模式,具体情况如何?

  JVM的存储器模型。

  为了理解Java对内存回收的处理方式,首先需要理解JVM内部的内存模型:


java


  首先看第一个图表:

  这个图表大致地描述了在JVM中运行线程涉及的5个部分。由于JVM是基于多线程机制的并行计算模型,每个线程都有自己独立的内存运行空间,线程和线程之间不会发生任何冲突,而虚拟机内存中的数据又被所有线程共享。你有一点晕吗?首先,要逐个理解每一个部分:

  (1).过程计数器。

  当指令运行时,程序计数器指向当前的运行线程A,它的字节码的位置或行号。当JVM执行多线程并行时,它的本质是多个线程之间的轮转,因为一个CPU同时只能执行一条指令,为了最大化地利用CPU的运行资源,所以使用了线程的轮转,当一个线程在空闲状态执行时,这个线程会被撤消,以代替其他线程,以防止该线程阻塞后续的线程请求,从而提高CPU的效率。因此,当线程A被撤下时,后面肯定还会重新切换上线程A继续执行,那么怎么知道在线程A被撤下之前执行线程的位置呢?这个函数就是程序计数器,它将当前执行的字节码的偏移值存储在线程内存中,这样就可以很好地后续执行。我们可以从中了解到,每个线程都有自己独立的程序计数器内存空间,用于指向自己的执行过程,因此这对于线程来说是私有的。

  若线程执行Java方法,则程序计数器指向虚拟机字节码的指令地址(行号);若执行本地方法,则计数器为空。


java


  (2)Java虚拟机栈。

  在线程中执行Java方法时,Java虚拟机栈描述了一种内存模型:当线程中执行Java方法时,每个Java方法都创建一个栈帧,它存储了本地变量表、操作栈等信息。在这些变量中,最关键的是本地变量表,它存储了基本的数据类型(short、int、long、byte、char、double、float、boolean),以及对象引用类型,等等,这些类型在Java方法执行时都是可以知道的。本地变量表的大小在编译期间就已确定,并且在执行时不修改表的大小。一定要注意,这个表也是线程私有的。

  (3)、局部方法堆栈。

  现在,Java虚拟机栈为线程中的Java方法服务,而本地方法栈则为线程中执行的本地方法服务,而本地方法使用则是存储与此相关的基本数据和其他信息。

  (4)、Java堆。

  JVM需要维护的最大内存区域是Java堆。这是一个由所有线程共享的内存区域。实际上,这个区域负责为所有对象实例分配内存空间,以及为数组分配空间。JVM是Java堆进行内存回收的主战场。目前主流的存储器回收算法也是以分代算法为主,在该区域内进行存储器划分。这个区域主要负责分配单个对象实例。

  (5)方法领域。

  在虚拟机装入时,方法区主要用来存储类信息、常量数据、静态变量和编译器编译后的相关方法的代码。所有线程都会共享这个区域。这类似于,当类装载静态变量时,它会被装入并被类共享。当单个线程需要该静态变量或静态方法时,将调用该方法。

  方法区的一个主要组成部分是常量池。池中存储由编译器生成的各种字面量和符号引用。

  谈到上面的5个部分,我们再来看下图,这很容易理解:
java

  通过对上述JVM内存模型的理解,可以有效地划分JVM的内存。本文中对Java堆进行了主要的划分。由于这一地区是GC回收的主战场。当执行内存回收时,Java堆主要分为2代,方法区(非堆)分为1代。这种分割方法是基于垃圾收集的分代回收机制的。这两代人在堆中划分为YoungGeneration和OldGeneration。

  YoungGeneration里分为3个区,分别是Eden区(Eden.Idens..这个名字我也很醉),FromSuvivor和ToSurvivor。在Eden中,主要用于为新创建的对象实例打开内存空间,其余两个区域FromSurvivor与Tosurvivor大小相同,主要用于存储经过一次垃圾回收后剩余的对象。

  OldGenneration中主要用来存储那些存活较长时间的对象。

  并且在方法区,即非堆区,将PermanentGeneration划分为若干块,用于存储一些类信息、静态字段和其他数据,当类开始装载时,这些数据将出现在类中。基本上没有参与垃圾回收。