欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  Java

JVM之内存管理

程序员文章站 2022-03-10 21:43:26
...
  都说搞C的牛叉,那是因为C解决问题,全靠程序员自己,他们对自己的程序在内存中是什么样了如指掌。而Java呢不需要有太多操作系统的知识,不用时刻注意内存的问题,但这不代表我们就不用去了解它背后的原理,(其实我是被公司的C嘲讽了一下OoO)。Java之所以容易上手,那是因为最困难的问题,已经被前人解决了,而这一切都归功于Java Virtual Machine-Java虚拟机,JVM其实就是一个抽象的计算机,它有自己的指令集,有自己的机器语言,有自己的内存管理。本系列会一一解开它的真面目。

  本文基于Java HotSpot™ 虚拟机,JDK 1.8,将讨论:

JVM 内部结构

JVM 内存管理

JVM 内存模型

JVM之内存管理

图 1 JVM 内部结构

1. JVM 内部结构

  一个程序执行的过程是这样的,以C语言为例,源代码首先被编译成可执行文件,以二进制的形式存放到磁盘上,当执行时,首先从磁盘加载到内存中,然后处理器就开始执行目标程序中机器指令。反观Java,首先编译成字节码文件,与平台无关,JVM通过ClassLoader加载到内存中,然后执行其中的机器指令,JVM帮我们跟操作系统打交道。有了字节码和JVM,Java实现了平台无关性。JVM也可以认为是一个进程,在启动时申请一块内存,然后按照功能的不同,把内存分为以下不同区域:

(1) Heap

  堆,一个非常重要的区域,被所有线程共享,基本所有的对象实例都在这里分配,大部分的垃圾回收也发生在这里。这部分内存,由JVM使用Garbage Collector(自动内存管理工具)来管理,职责就是为对象分配内存,释放空闲内存。Java 堆的大小可以使用参数来控制是固定的,还是动态扩展的。

(2) JVM Stacks

  栈,与线程息息相关,线程私有,随线程生而生,死而死,是线程执行工作的内存。HotSpot中Java栈和本地方法栈合二为一,都在本地内存空间中分配,这部分内存就不需要JVM刻意的去管理了。JVM栈主要用来存储栈帧,当调用一个方法时创建一个栈帧,方法结束销毁,从栈的角度来看,就是入栈和出栈两个操作。

  栈帧是一个数据结构,用来存储局部变量,操作数栈,和当前类运行时常量池的引用。局部变量数组:用来保存方法内定义的基本类型变量,下标从 0 开始,JVM使用局部变量表传递方法参数,当调用一个实例方法,第 0 位置存储的是当前对象的 this 引用;操作数栈:用来执行运算和准备调用方法的参数以及方法的返回结果;动态链接:引用对象的运行时常量池。

(3) PC Register

  程序计数器,线程私有,主要作用就是存储指令地址,取指,解码和执行。每个线程都关联着唯一的栈和PC寄存器。

(4) Metaspace

  元空间,在JDK8之前的HotSpot VM称之为方法区或者永久代,被各线程共享,它存储了一个类的结构信息,如常量池、字段、方法等。存放在本地内存中,与堆不相关。

(5) Native Method Stacks

  JVM栈是为Java方法准备的,那么本地方法栈则是为虚拟机调用本地方法服务的。

2. JVM 内存管理

  Java不允许直接操作内存,内存的申请和释放统一交给虚拟机处理。

2.1 Garbage Collection 自动内存管理

  自动内存管理(以下简称GC)的职责:

分配内存

确保引用对象保留在内存

回收不可达引用对象的内存

  GC 解决了大部分的内存分配问题,它本身也占用一定的资源。当堆满了或者是它的某个组成部分达到一个阀值就会触发垃圾回收。垃圾回收主要从这几方面考虑:回收的频率和时间,比如堆小那么回收的次数就多,堆大回收次数少,但回收一次的时间长;内存碎片问题;多线程程序下的垃圾回收。

垃圾回收策略:

(1)串行与并行(Serial versus Parallel)

  串行,同一时间只能有一个垃圾回收线程工作;并行,在多CPU系统中,可有多个垃圾回收线程同时工作。

(2)并发与Stop-the-world(Concurrent versus Stop-the-world)

  Stop-the-world 垃圾回收器,在回收期间,应用程序完全暂停工作,此时堆就相当于被冻结了,对象的状态不可变;并发,一个或多个垃圾收集任务与应用程序同时执行,可能会出现短暂的Stop-the-world,在收集时,对象的状态可能会改变。

(3)复制(Copying)

  将内存分为两半,回收时将存活的对象复制到另一半空间,然后清除当前内存,后续内存的分配比较容易,但内存的利用率比较低。

(4) 标记清除与标记整理(Compacting versus Non-compacting)

  标记清除,标记可回收的对象,统一进行回收,不进行内存压缩,会产生大量的内存碎片,在分配大对象时,可能无法找到连续的内存;标记整理,在标记完后,首先对内存进行压缩整理,把所有存活的对象放到一起进行回收。

(5)分代收集

  把堆分成几个区域,新生代和老年代,不同的区域使用上面的不同的回收方法。

2.2 HotSpot 中的分代收集

  在 HotSpot 中,把内存分为新生代和老年代,新生代又分为Eden和两个大小一样的Survivor空间,大部分对象在Eden分配,一些大对象可能直接分配到老年代。

堆的结构如下:

JVM之内存管理

图 2 堆

相关标签: Java