Java的内存结构(Memory Structure)和垃圾收集(Garbage Collection)图解
- 格式:docx
- 大小:115.91 KB
- 文档页数:4
你必须了解的java内存管理机制(四)-垃圾回收本⽂在个⼈技术博客不同步发布,详情可亦可扫描屏幕右侧⼆维码关注个⼈公众号,公众号内有个⼈联系⽅式,等你来撩...相关链接(注:⽂章讲解JVM以Hotspot虚拟机为例,jdk版本为1.8)1、2、3、4、前⾔ 在前⾯三篇⽂章中,对JVM的内存布局、内存分配、垃圾标记做了较多的介绍,垃圾都已经标记出来了,那剩下的就是如何⾼效的去回收啦!这篇⽂章将重点介绍如何回收旧⼿机、电脑、彩电、冰箱~啊呸(⊙o⊙)…将重点介绍⼏种垃圾回收算法、HotSpot中常⽤的垃圾收集器的主要特点和应⽤场景。
同时,这篇⽂章也是这个系列中的最后⼀篇⽂章啦!正⽂ 上⼀篇⽂章中,我们详细介绍了两种标记算法,并且对可达性分析算法做了较多的介绍。
我们也知道了HotSpot在具体实现中怎么利⽤OopMap+RememberedSet的技术做到“准确式GC”。
不管使⽤什么优化的技术,⽬标都是准确⾼效的标记回收对象!那么,为了⾼效的回收垃圾,虚拟机⼜经历了哪些技术及算法的演变和优化呢?(注:G1收集器及回收算法本⽂不涉及,因为我觉得后⾯可以单独写⼀篇⽂章来谈!)回收算法 在这⾥,我们会先介绍⼏种常⽤的回收算法,然后了解在JVM中式如何对这⼏种算法进⾏选择和优化的。
标记-清除 "标记-清除"算法分为两个阶段,“标记”和“清除”。
标记还是那个标记,在上⼀篇⽂章中已经做了较多的介绍了,JVM在执⾏完标记动作后,还在"即将回收"集合的对象将被统⼀回收。
执⾏过程如下图: 优点: 1、基于最基础的可达性分析算法,它是最基础的收集算法。
2、后续的收集算法都是基于这种思路并对其不⾜进⾏改进⽽得到的。
缺点: 1、执⾏效率不⾼。
2、由上图能看到这种回收算法会产⽣⼤量不连续内存碎⽚,如果这时候需要创建⼀个⼤对象,则⽆法进⾏分配。
复制算法 “复制”算法将内存按容量划分为⼤⼩相等的两块,每次使⽤其中的⼀块。
JVM体系结构之⼀:总体介绍⼀、Java的内存区域划分Java 虚拟机在执⾏Java程序的时候会把它管理的内存区域划为⼏部分,这⼀节我们就来解析⼀下Java的内存区域。
Java的内存区域主要分为五部分:程序计数器(PC)Java 虚拟机栈(JVM Stack)本地⽅法栈(Native Method Stack)Java 堆内存(Java Heap)⽅法区(Method Area)1.1、按照功能1.2、按照内存是否共享类装载器(ClassLoader)(⽤来装载.class⽂件)执⾏引擎(执⾏字节码,或者执⾏本地⽅法)运⾏时数据区(⽅法区、堆、java栈、PC寄存器、本地⽅法栈),如下图:下⾯我们来解析这⼏个区域。
1、程序计数器程序计数器(Program Counter Register)是⼀块较⼩的内存空间,它的作⽤可以看做是当前线程所执⾏的字节码的信号指⽰器。
每⼀条JVM线程都有⾃⼰的PC寄存器在任意时刻,⼀条JVM线程只会执⾏⼀个⽅法的代码。
该⽅法称为该线程的当前⽅法(Current Method)如果该⽅法是java⽅法,那PC寄存器保存JVM正在执⾏的字节码指令的地址,如果该⽅法是native,那PC寄存器的值是undefined。
此内存区域是唯⼀⼀个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。
2、Java 虚拟机栈Java虚拟机栈也是线程私有的,每⼀条线程都拥有⾃⼰私有的Java虚拟机栈,它与线程同时创建。
并且⽣命周期与线程相同。
它描述了Java⽅法执⾏的内存模型:每个⽅法在执⾏的同时都会创建⼀个栈帧,⽤于存储局部变量表、操作数栈、动态链接、⽅法出⼝等信息。
每⼀个⽅法从调⽤直⾄完成的过程,都对应⼀个栈帧从⼊栈到出栈的过程。
关于栈帧详细的内容在后边复习虚拟机字节码执⾏引擎的时候再说吧。
Java 虚拟机栈在⽅法调⽤和返回中也扮演了很重要的⾓⾊。
因为除了栈帧的⼊栈和出栈之外,Java虚拟机栈不会再受其它因素的影响,因此栈帧可在系统的堆上分配内存(注意,是系统的Heap⽽不是Java Heap)。
Java虚拟机(JVM)的内存垃圾回收机制主要涉及自动内存管理和垃圾回收两个核心功能。
自动内存管理主要是针对对象内存的回收和对象内存的分配。
JVM的堆是垃圾收集器管理的主要区域,也被称作GC堆(Garbage Collected Heap)。
大部分情况下,对象都会首先在Eden区域分配。
在一次新生代垃圾回收后,如果对象还存活,则会进入s0或者s1,并且对象的年龄还会加1(Eden区->Survivor区后对象的初始年龄变为1),当它的年龄增加到一定程度(默认为15岁),就会被晋升到老年代中。
垃圾回收是JVM自动内存管理的另一个重要方面。
主要有两种通用的垃圾回收方法:引用计数法和可达性分析算法。
引用计数法为每个对象增加一个计数器,当该对象被引用时,计数器加一,当引用失效时,计数器减一。
当计数器为零时,该对象就可以被回收了。
这种方法无法解决循环引用的问题。
可达性分析算法是通过GC Root的对象作为起始节点,通过引用向下搜索,所走过的路径称为引用链。
当对象没有任何一条引用链链接的时候,就会被认定为垃圾。
可作为GC Root的对象包括:类加载器,Thread,虚拟机栈的本地变量表,static成员,常量引用,本地方法栈的变量等等。
大部分情况下,对象都会首先在Eden区域分配,在一次新生代
垃圾回收后,如果对象还存活,则会进入s0或者s1,并且对象的年龄还会加1(Eden区->Survivor区后对象的初始年龄变为1),当它的年龄增加到一定程度(默认为15岁),就会被晋升到老年代中。
以上信息仅供参考,如果还想了解更多信息或遇到相关问题,建议咨询专业人士。
JVM 内存包含如下几个部分:Heap Memory 存放Java对象Non-Heap Memory 存放类加载信息和其它meta-dataOther 存放JVM 自身代码等在JVM启动时,就已经保留了固定的内存空间给Heap内存,这部分内存并不一定都会被JVM 使用,但是可以确定的是这部分保留的内存不会被其他进程使用。
这部分内存大小由 -Xmx 参数指定。
而另一部分内存在JVM启动时就分配给JVM,作为JVM的初始Heap内存使用。
影响这个的参数是 -Xms。
默认空余堆内存小于40%时,JVM 就会增大堆直到-Xmx的最大限制,可以由-XX:MinHeapFreeRatio指定。
默认空余堆内存大于70%时,JVM 会减少堆直到-Xms的最小限制,可以由-XX:MaxHeapFreeRatio指定。
可以通过-XX:MaxPermSize设置Non-Heap大小.GC 的年代划分如果-Xms指定的值比-Xmx的小,那么两者的差值就是Virtual内存值。
随着程序的运行,Eden区、 Tenured区和Perm区会逐渐使用保留的Virtual空间。
JVM内存模型中Heap区分两大块,一块是 NEW Generation,另一块是Old Generation. 在NewGeneration 中,有一个叫Eden的空间,主要是用来存放新生的对象,还有两个Survivor Spaces(from,to),它们的大小总是一样,它们用来存放每次垃圾回收后存活下来的对象。
在OldGeneration中,主要存放应用程序中生命周期长的内存对象。
在NewGeneration块中,垃圾回收一般用Copying的算法,速度快。
每次GC 的时候,存活下来的对象首先由Eden拷贝到某个SurvivorSpace, 当Survivor Space空间满了后, 剩下的live对象就被直接拷贝到OldGeneration中去。
因此,每次GC后,Eden内存块会被清空。
java内存分配与回收机制Java是一种面向对象的编程语言,它具有自动内存管理的特性,这意味着开发人员不需要手动分配和释放内存。
Java的内存分配和回收机制是由Java虚拟机(JVM)来管理的,它使用垃圾回收器来自动回收不再使用的内存。
内存分配:在Java程序中,内存分配是由JVM的堆内存来完成的。
堆内存用于存储对象实例和数组。
当我们创建一个对象时,JVM会在堆内存中为该对象分配内存空间。
堆内存被划分为新生代和老年代,新生代又分为Eden区和两个Survivor区。
当一个对象被创建时,它会被分配到Eden区,随着时间的推移,经过几次垃圾回收后,仍然存活的对象会被移到Survivor区,最终会被移到老年代。
内存回收:Java的垃圾回收器负责自动回收不再使用的内存。
垃圾回收器会定期扫描堆内存,找出不再被引用的对象,并释放它们占用的内存空间。
垃圾回收器使用不同的算法来进行垃圾回收,包括标记-清除算法、复制算法和标记-整理算法等。
垃圾回收器的工作原理是基于对象的可达性分析,如果一个对象不再被任何引用所指向,那么它就成为垃圾,垃圾回收器会将其回收并释放内存。
垃圾回收器的性能对程序的性能有着直接的影响,因此Java提供了不同的垃圾回收器以满足不同场景下的需求。
总结:Java的内存分配和回收机制是由JVM来管理的,开发人员不需要手动管理内存。
JVM使用堆内存来存储对象实例和数组,并使用垃圾回收器来自动回收不再使用的内存。
了解Java的内存分配和回收机制有助于开发人员编写高效的Java程序,避免内存泄漏和性能问题。
java原理面试题一、介绍Java是一种高级编程语言,由Sun Microsystems(现为Oracle Corporation)于1995年推出。
作为一种面向对象的语言,Java在很多领域都有广泛的应用。
本文将介绍Java的一些基本原理,并提供一些常见的Java原理面试题。
二、Java虚拟机(JVM)1. 什么是JVM?JVM全称为Java虚拟机(Java Virtual Machine),是Java程序的运行环境。
它将Java字节码(由Java编译器生成的中间代码)解释或编译成机器代码,使得Java程序能够在不同的操作系统和硬件平台上运行。
2. JVM的组成部分有哪些?JVM主要由以下几个组成部分构成:- 类加载器(ClassLoader):负责将字节码加载到JVM中。
- 运行时数据区(Runtime Data Area):包括方法区、堆、栈等。
- 执行引擎(Execution Engine):解释或编译字节码进行执行。
- 本地方法接口(Native Interface):与本地库进行交互的接口。
- 垃圾回收系统(Garbage Collection System):用于自动回收无用的对象。
3. JVM的工作原理是什么?JVM首先通过类加载器将字节码加载到内存中的方法区中。
然后,它通过执行引擎解释或编译字节码,将其转换为对应平台的机器代码。
执行引擎将机器代码交给处理器执行,并将结果返回给JVM。
同时,垃圾回收系统负责回收无用的对象,释放内存空间。
三、Java内存管理1. 什么是Java内存模型?Java内存模型(Java Memory Model,JMM)定义了Java程序在内存中的存储方式和访问规则。
它规定了线程如何与主内存以及其他线程之间进行通信。
2. Java中的内存分区有哪几种?Java内存分为以下几个区域:- 方法区(Method Area):用于存储类的结构信息和静态变量。
- 堆(Heap):存储对象实例。
jvm堆的基本结构
Java虚拟机(JVM)堆是一种重要的内存分配结构,被用来存储Java 类实例和数组,是Java内存管理的重要组成部分。
JVM堆由以下三部分组成:
1.堆栈:堆栈是一种先进后出(LIFO)的内存结构,用于存储Java对象的本地变量。
堆栈空间占用资源比较小,但容量有限,一般比较小(只支持少计数的变量)。
2.程序计数器:程序计数器是一个小巧且独立的内存结构,用于保存执行过程中当前活动线程正在执行的字节码行号。
jvm通过程序计数器控制程序运行,它不会存储任何对象。
3.垃圾回收堆:垃圾回收堆是一种用于存储对象的内存结构,一般由堆顶(Young generation),年老代(Old Generation )和永久代(Permanent Generation)组成。
堆顶是一个存储新生成的对象的内存区域,当堆顶达到容量上限时,部分对象会被转移至年老代;而永久代则用于存放永久数据,如Java类,字段和方法。
总的来说,JVM堆是一个内存结构,用于管理Java对象。
它主要由堆栈、程序计数器和垃圾回收堆组成,通过这三个基本构建块构成JVM
堆,兼顾性能和可维护性。
JVM堆是Java内存管理的重要组成部分,其利用了可伸缩性和性能可控性,是运行Java程序的重要基础。
JVM内存区域(Java内存区域)、JVM垃圾回收机制(GC)初探⼀、JVM内存区域(Java内存区域) ⾸先区分⼀下JVM内存区域(Java内存区域)和Java内存模型(JMM)的概念。
Java线程之间的通信采⽤的是共享内存模型,这⾥提到的共享内存模型指的就是Java内存模型(简称JMM),Java内存模型(即Java Memory Model,简称JMM)本⾝是⼀种抽象的概念,并不真实存在;Java线程之间的通信由JMM控制,JMM决定⼀个线程对共享变量的写⼊何时对另⼀个线程可见。
⽽JVM内存区域,有的地⽅也称之为Java内存区域,是JVM对JMM的实现。
在JVM内部,Java内存模型把内存分成了两部分:线程栈区和堆区,也可以理解为线程共享内存区和线程私有内存区。
接下来就具体介绍分析⼀下JVM内存区域。
Java程序是交由JVM执⾏的,所以我们在谈Java内存区域划分的时候事实上是指JVM内存区域划分。
在讨论JVM内存区域划分之前,先来看⼀下Java程序具体执⾏的过程: 从图中可以看到,JVM主要由三⼤部分组成:类加载器、执⾏引擎、运⾏时数据区。
本篇博客主要介绍运⾏时数据区部分,类加载器会后续单独在⼀篇博客中介绍。
如上图所⽰,⾸先Java源代码⽂件(.java后缀)会被Java编译器编译为字节码⽂件(.class后缀),然后由JVM中的类加载器中的defineClass()⽅法加载各个类的字节码⽂件进jvm内存中,⽣成ng.Class对象,每个类在JVM中都拥有⼀个对应的ng.Class 对象,并存放于运⾏时数据区(Runtime Data Area)的堆中,它提供了类结构信息的描述。
数组、枚举及基本Java类型(如int、double等)甚⾄void都拥有对应的Class对象。
这也就是我们常说的Java类的静态加载,即程序在运⾏时,所需要的类就必须加载好,如果编译时这个类的.class⽂件不存在,程序将编译出错⽆法运⾏;还有⼀种是动态加载是通过反射机制在运⾏时⽤Class.forName()加载字节码⽂件获得ng.Class对象并放到JVM内存中。
JVM的内存管理机制详解JVM(Java Virtual Machine)是Java编程语言的基础,它允许Java应用程序在不同的操作系统上运行。
JVM负责将Java字节码翻译成机器可执行的指令,并管理Java应用程序的内存。
JVM的内存管理机制包括垃圾回收、内存分配和内存优化等方面。
下面将详细介绍JVM的内存管理机制。
1. 堆内存(Heap Memory):堆内存是JVM中最大的一块内存区域,用于存储对象实例。
我们创建的所有对象都存放在这个区域中。
堆内存由新生代和老年代组成。
新生代又分为Eden区和两个Survivor区,用于存放新创建的对象,而老年代存放存活时间较长的对象。
2. 栈内存(Stack Memory):栈内存用于存储Java方法的局部变量、方法参数和临时变量。
每个线程在执行方法的时候都会创建一个栈帧,栈帧包含了方法的局部变量和操作数栈。
栈帧的大小在方法编译时就确定了,因此栈内存的分配和回收是非常快速和高效的。
3. 方法区(Method Area):方法区用于存储已加载的类信息、常量、静态变量和编译后的代码等数据。
方法区在JVM启动时被创建,并且在JVM关闭时销毁。
方法区中存放的数据是共享的,所有线程共享同一块方法区内存。
4. 本地方法栈(Native Method Stack):本地方法栈用于存储Java应用程序调用本地方法的相关信息。
本地方法栈和栈内存的作用类似,不同之处在于本地方法栈存储的是本地方法调用相关的数据。
5. PC寄存器(Program Counter Register):PC寄存器用于存储当前线程执行的字节码指令地址。
每个线程都有独立的PC寄存器,用于控制线程的执行。
6. 垃圾回收(Garbage Collection):垃圾回收是JVM的一个重要特性,用于自动回收不再使用的对象和释放内存空间。
JVM中的垃圾回收器会定期扫描堆内存,将不再使用的对象标记为垃圾,并进行回收。
JVM 内存包含如下几个部分:
∙Heap Memory 存放Java对象
∙Non-Heap Memory 存放类加载信息和其它meta-data
∙Other 存放JVM 自身代码等
在JVM启动时,就已经保留了固定的内存空间给Heap内存,这部分内存并不一定都会被JVM使用,但是可以确定的是这部分保留的内存不会被其他进程使用。
这部分内存大小由-Xmx参数指定。
而另一部分内存在JVM启动时就分配给JVM,作为JVM的初始Heap 内存使用。
影响这个的参数是-Xms。
默认空余堆内存小于40%时,JVM 就会增大堆直到-Xmx的最大限制,可以由
-XX:MinHeapFreeRatio指定。
默认空余堆内存大于70%时,JVM 会减少堆直到-Xms的最小限制,可以由
-XX:MaxHeapFreeRatio指定。
可以通过-XX:MaxPermSize设置Non-Heap大小.
GC 的年代划分
如果-Xms指定的值比-Xmx的小,那么两者的差值就是Virtual内存值。
随着程序的运行,Eden区、Tenured区和Perm区会逐渐使用保留的Virtual空间。
JVM内存模型中Heap区分两大块,一块是NEW Generation,另一块是Old Generation. 在NewGeneration中,有一个叫Eden的空间,主要是用来存放新生的对象,还有两个Survivor Spaces (from,to),它们的大小总是一样,它们用来存放每次垃圾回收后存活下来的对象。
在OldGeneration中,主要存放应用程序中生命周期长的内存对象。
在NewGeneration块中,垃圾回收一般用Copying的算法,速度快。
每次GC的时候,存活下来的对象首先由Eden拷贝到某个SurvivorSpace, 当Survivor Space
空间满了后, 剩下的live对象就被直接拷贝到OldGeneration中去。
因此,每次GC后,Eden内存块会被清空。
在OldGeneration块中,垃圾回收一般用mark-compact的算法,速度慢些,但减少内存要求.
垃圾回收分多级,0级为全部(Full)的垃圾回收,会回收OLD段中的垃圾;1级或以上为部分垃圾回收,只会回收NEW中的垃圾,内存溢出通常发生于OLD段或Perm段垃圾回收后,仍然无内存空间容纳新的Java 对象的情况。
还有个Permanent Generation,主要用来放JVM自己的反射对象,比如类对象和方法对象等。
关于这个区,它还提供String pool,看下面的例子:
[java]view plaincopyprint?
1.String first = "abc";
2.String second = new String ("abc");
1.String s = "abc";
2.String p = "abc";
1. JVM 会试图为相关Java对象在Eden中初始化一块内存区域
2. 当Eden空间足够时,内存申请结束。
否则到下一步
3. JVM 试图释放在Eden中所有不活跃的对象(这属于1或更高级的垃圾回收),释放后若Eden
空间仍然不足以放入新对象,则试图将部分Eden中活跃对象放入Survivor区
4. Survivor区被用来作为Eden及OLD的中间交换区域,当OLD区空间足够时,Survivor区的
对象会被移到Old区,否则会被保留在Survivor区
5. 当OLD区空间不够时,JVM 会在OLD区进行完全的垃圾收集(0级)
6. 完全垃圾收集后,若Survivor及OLD区仍然无法存放从Eden复制过来的部分对象,导致JVM
无法在Eden区为新对象创建内存区域,则出现”out of memory错误”
具体算法请参考:JDK5.0中JVM堆模型、GC垃圾收集详细解析。