• 技术文章 >Java >java教程

    JVM之内存管理

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

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

    JVM 内部结构

    JVM 内存管理

    JVM 内存模型

    t0128f824aa7ed65fdd.png

    php入门到就业线上直播课:进入学习

    图 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分配,一些大对象可能直接分配到老年代。

    堆的结构如下:

    t0128f824aa7ed65fdd.png

    图 2 堆

    声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。

    前端(VUE)零基础到就业课程:点击学习

    清晰的学习路线+老师随时辅导答疑

    自己动手写 PHP MVC 框架:点击学习

    快速了解MVC架构、了解框架底层运行原理

    专题推荐: Java
    上一篇:java单词及java关键词 下一篇:自己动手写 PHP MVC 框架(40节精讲/巨细/新人进阶必看)

    相关文章推荐

    • ❤️‍🔥共22门课程,总价3725元,会员免费学• ❤️‍🔥接口自动化测试不想写代码?• Java数据结构之单链表与OJ题• 详细介绍Java正则表达式之单字符匹配和预定义字符• Java总结分享之反射、枚举、Lambda表达式• 实例详解Java顺序表和链表• 深入分析Java的序列化与反序列化
    1/1

    PHP中文网