Java中的内存分配与垃圾回收

一、内存分配

Java程序运行时的内存分配,按照JVM规范,包括以下几个区域:程序计数器、虚拟机栈、本地方法栈、方法区、堆。其中,前三个是线程私有的,与线程生命周期相同,线程退出内存自动回收;后两者是所有线程共享内存的,只在垃圾回收机制被触发时,被动回收。

* 程序计数器,内存区域极小,是当前线程的字节码执行行号指示器;

* 虚拟机栈、本地方法栈,即平时所说的“栈”,是虚拟机用来执行方法(包括Java、非Java方法)时,使用的临时内存空间,用来存储当前方法、局部变量等,全部基本类型变量,以及类对象的引用都存储在栈中;

* 方法区,全局共享区域,用来存储已经被虚拟机加载的Class信息、常量(如字符串字面常量)、静态变量,以及编译器编译后的代码等;

* 堆,是Java虚拟机管理中最大的一块内存,为所有线程所共享,用来存储所有Java类实例。需要注意的是,实例数据在堆中开辟内存,而对象的引用相当于指针,存储在各线程的栈中。

二、垃圾回收算法

垃圾回收需要解决的三个问题:

1)哪些内存可回收。上面各内存区域中,只有方法区、堆是全局共享的内存块,需要垃圾回收来处理,定时回收内存。确定某个对象是否可回收,需要通过“可达性分析算法”来判断,哪些内存块是孤立无源、不可达的对象或对象集合。

2)什么时间能执行回收。执行垃圾回收时,需要完全地“冻结”现场,达到所谓“stop the world”,以避免在GC的过程中,引用关系发生变化,而引发问题。虚拟机的做法是让所有线程主动式中断,但并非立刻强制中断线程,而是GC线程设置一个标识,让其它所有线程在到达其下一个“安全点”时,根据此标识而主动挂起线程,等待GC线程的操作;

3)如何执行垃圾回收。垃圾回收算法有很多,但总体策略是分代回收,针对新生代、老生代特点,采取的GC算法是不同的。

* 对于新生代内存块,对象存活率低,需要频繁清理,为了避免“标记-清除”算法的碎片化问题,也为了提高效率,采取“复制”算法,即将新生代(young区)分为eden+survivor1+survivor2几块空间,在进行GC时,将eden/survivor1中的存活对象,复制到survivor2空间中暂存,然后整块清理掉前面的eden+survivor1区域,回收这块通常占比90%的内存区域。

* 对于老生代内存块,对象存活率高,不需要进行频繁的存活性判断的扫瞄,当进行GC时,通常使用“标记-整理”算法:标记可回收区域、移动集中存活对象内存、最终对剩余的垃圾区域进行回收。

三、垃圾收集器实现

GC线程在工作时,会完全或部分中断用户线程,影响系统吞吐率,使用户任务出现停顿甚至卡死。新生代的收集器有Serial/ParNew/Parallel Scavenge,老生代的收集器有CMS/Serial Old/Parallel Old。

* Serial/Serial Old是单线程收集器,在进行GC时,会中断所有用户线程,在新、老生代分别采用复制算法和标记-整理算法进行垃圾清理;

* ParNew是多个GC线程并行,但同样会中断所有用户线程,算法层面和Serial系列一致;

* Parallel收集器,同样也是多线程并行的回收器,不同的是其关注的是系统的吞吐率的指标,即用户任务的停顿时间比例,而不是停顿的绝对时间。

* CMS(Concurrent Mark Sweep)收集器,目的是追求最短停顿时间,目前流行最为广泛的收集器实现。分为initial mark/concurrent mark/remark/concurrent sweep四个步骤。其中,initial mark仅标记一下GC Roots能直接关联到的对象,concurrent mark是多GC线程同时与用户线程进行并发,进行GC Roots Traceing,实质是存活对象的联通域生长与标记,remark阶段对concurrent mark阶段用户线程并发执行时的对象引用变动,进行修正,concurrent sweep是并发地对垃圾进行清理。第1、3步,所有用户线程依然会中断,但因为其操作十分简单,所以速度很快,第2、4步,耗时较长,但是与用户线程并发进行的,因此整体来看,CMS GC的用户任务停顿时间极短。

时间: 2024-10-10 05:20:58

Java中的内存分配与垃圾回收的相关文章

Java中的内存分配机制

Java的内存分为两种:一种是栈内存,一种是堆内存. 在函数中定义的一些基本类型变量和对象的引用都在函数的栈内存中分配.当在一个代码块中定义一个变量的时候,java就在栈中为其分配内存,当超过作用域的时候内存自动释放. 对内存用来存放new创建的对象和数组.在堆中分配的内存,由java虚拟机的垃圾回收机器管理.java的堆是运行时数据区,堆的优势是可以动态的分配内存大小,生存周期也不必事先告诉编译器,但是,由于是动态分配,存取速度慢. 栈的优势是比堆的存取速度快,仅次于寄存器,栈数据可以共享,但

Node.js的内存分配和垃圾回收

简单介绍Node.js的内存分配和垃圾回收 内存分配 Node.js是一个由JavaScript V8引擎控制的C++程序V8的内存管理模式一个运行的程序通常是通过在内存中分配一部分空间来表示的.这部分空间被称为驻留集(Resident Set).V8的内存管理模式有点类似于Java虚拟机(JVM),它会将内存进行分段: 代码 Code:实际被执行的代码 栈 Stack:包括所有的携带指针引用堆上对象的值类型(原始类型,例如整型和布尔),以及定义程序控制流的指针. 堆 Heap:用于保存引用类型

java中的内存分配原则

问题:Java中这些类.变量.字符串.方法,在内存中是怎样分配的? 首先,Java中的内存区域如下: ◆寄存器:我们在程序中无法控制 ◆栈:存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中.当在一段代码块定义一个变量时,Java就在栈中 为这个变量分配内存空间,当该变量退出该作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用.实际上,栈中的变量指向堆内存中的变量,这就是Java中的指针! ◆堆:存放用new产生的数据.在堆中分配的内存,由J

jvm 深入理解自动内存分配与垃圾回收

要想了解jvm自动内存分配,首先必须了解jvm的运行时数据区域,否则如何知道在哪里进行自动内存分配,如何进行内存分配,回收哪里的垃圾对象? jvm运行时数据区:程序计数器,虚拟机栈,本地方法栈,方法区,堆 程序计数器:由于程序指令是一条一条顺序执行,一条执行完之后必须知道下一条该执行那条指令,那么程序计数器就是来记录下一条指令的地址,如果调用本地方法,则程序计数器记录空值,还有由于java线程由cpu调度并发执行,所以程序计数器也有助于线程状态的恢复,程序计数器如果线程共享,在频繁的线程调度下保

Java进阶之内存管理与垃圾回收

Java是在JVM所虚拟出的内存环境中运行的.内存分为栈(stack)和堆(heap)两部分.我们将分别考察这两个区域. 栈 在Java中,JVM中的栈记录了线程的方法调用.每个线程拥有一个栈.在某个线程的运行过程中,如果有新的方法调用,那么该线程对应的栈就会增加一个存储单元,即帧(frame).在frame中,保存有该方法调用的参数.局部变量和返回地址. 调用栈 Java的参数和局部变量只能是基本类型的变量(比如int),或者对象的引用(reference).因此,在栈中,只保存有基本类型的变

Java内存分配与垃圾回收

1.JVM管理的内存包含下图所示的几个运行时数据区域,其中方法区和堆为线程共享的数据区域,程序计数器,虚拟机栈以及本地方法栈为线程私有的数据区域. 程序计数器:可以看做是当前线程所执行的字节码的行号指示器,告诉字节码解释器该读取哪条指令 虚拟机栈:生命周期和线程相同,每个方法在执行的同时都会创建一个栈帧,用于存储局部变量表,操作数栈,动态链接,方法出口等信息,每一个方法从调用到完成的过程就对应了一个栈帧在虚拟机中入栈到出栈的过程.栈中存放了编译器可知的各种基本数据类型和对象引用. 本地方法栈:与

Java 内存分配及垃圾回收机制初探

一.运行时内存分配 Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域. 这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则依赖用户线程的启动和结束而建立和销毁. 线程私有区域(生命周期与线程相同) a)  虚拟机栈 虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧(Stack Frame[1])用于存储局部变量表. 操作数栈. 动态链接. 方法出口等信息. 每一个方法从调用直至执行完成的

Java 中的内存分配

java中内存主要包含4块,堆内存区.栈内存区.常量池.方法区. 堆内存中:存放的是new出的对象(引用类型也是对象,比如String,数组),new出的对象只包含成员变量. 栈内存中:存放的是局部成员变量.对于基本的数据类型存放的是基本变量的值,而对于对象变量,存放的是堆内存的地址. 常量池:存放的是静态变量(类变量)或是常量. 方法区:存放的是对象的方法.因此即使new出多个对象也是只是存在一个方法. 原文地址:https://www.cnblogs.com/jijijijibeibeibe

Java中的内存分配

Java程序在运行时,需要在内存中分配空间,为了提高效率,就对空间进行了不同区域的划分,因为每一片区域否有特定的处理数据方式和内存管理方式. 1.栈存储局部变量 2.堆存储new出来的东西 3.方法区(后面讲) 4.本地方法区(和系统相关) 5.寄存器(给CPU使用) 局部变量:在方法定义中或者方法声明上的变量都称为局部变量.