JVM探索之——内存管理(二)

上篇文章我们介绍了JVM所管理的内存结构也就是运行时数据区(Run-Time Data Areas),现在我们将介绍JVM的内存分配与回收
静态内存分配与动态内存分配

JVM的内存分配主要分为两种:静态内存分配与动态内存分配与之对应的是基本类型内存分配与对象内存分配;
1、静态内存分配
       静态内存分配在编译时已确定好内存空间,程序载入时JVM把一次内存分配给它,此后不会再发生变化。这些内容包括:方法中的局部变量(基本数据类型)、类变量(基本数据类型)、对象的引用;对于方法中的局部变量是存储在Java栈的局部变量表中,方法执行结束栈帧出栈,局部变量也会跟着收回内存空间;而类变量是存储在方法区中的,这里的内存回收时间是不确定的。
2、动态内存分配
      Java里给对象分配内存是动态分配的,编译的时候并不能确定对象的存储空间大小,只是创建对象时才能进行内存空间分配,而内存空间的回收是在对象不再被引用时才能回收。对象是存储在堆中,只有当GC触发时才清理回收那些没有被引用的对象的内存空间;
如下面代码段;
    public class Test {
      byte[] bytes=new byte[1024*1024*5];
      long a=1000;
      public static void main(String[] args){
        long b=2;
        byte[] base=new byte[1024*1024*1];
        Test t=new Test();
      }
    }
    bytes:是数组内存分配在堆中的,但引用存储才方法区中,JVM将使用动态内存分配,编译时无法确定存储空间只有当创建test对象时才为bytes分配存储空间,只有当该对象没有被引用时才能被GC回收;
    a:是类变量,基本数据类型,当Test编译载入时就确定了存储空间,存储在方法区中;
    b:是方法中的基本数据类型局部变量只有执行该方法时才在栈帧的局部变量表中分配存储空间方法执行完成后就释放该栈帧,也回收局部变量表中的存储空间;
    base:局部变量,只有执行到该语句时才在堆中为它分配存储空间,引用存储在栈帧局部变量表中没有被引用时才能被GC回收;
    t:对象与数组一样在堆中分配存储空间,但对象引用存储在栈帧局部变量表中,对象没有被引用时GC才能回收存储空间;

确定是否可被回收
    接下来我们将介绍JVM的内存回收,JVM在进行垃圾回收时首先要做的是确定哪些是垃圾,然后才能回收垃圾所占用的内存空间。上面我们说过,对象要是被回收内存的前提是该对象没被活动的对象引用,那JVM是怎么知道一个对象是否被引用的呢。
一般用于确定对象是否被引用的算法有两种:引用计数算法、根搜索算法;
    引用计数算法(Reference Counting):在对象中添加个引用计数器,当有引用时对象计数器加一,引用消失时,计数器减一,只要引用计数器为0时该对象就是没有被引用的,比少编程语言使用该算法,如:python等。
    根搜索算法(GC Roots Tracing):Java、C#等使用该算法判断对象是否存活,该算法从一系列为“GC Roots”的对象为起点,从这些节点开始搜索,走过的路径为引用链(Reference Chain),当对象到GC Roots无任何引用链时,此对象就是不可用的,此不可用的对象将作为回收的对象。


               1、2、3为活动对象,三个对象到GC Roots均有引用链
               4、5 为死对象,对象到GC Roots不可达

Java中,可用于作为GC Roots的对象有:
        ? 虚拟机栈的栈帧中局部变量表中引用的对象
        ? 本地方法栈中的引用对象
        ? 方法区中常量引用的对象
        ? 方法区的中的类静态属性引用的对象
        ? 类的Class对象的引用

垃圾收集算法
    1、 标记-清除算法(Mark-Sweep),算法分为了标记、清除两个阶段,他根据我们前面所讲的方法判断对象是否可被回收,如可回收则标记起来,待标记完所有对象后统一进行回收;
    2、 复制算法(Copying)复制收集算法把内存分为两块容量相同的空间,每次只使用一快,当一快用完时就将还存活的对象复制到另一块中,然后把原来使用过的内存空间进行回收。
    3、 标记-整理算法(Mark-Compact)该算法与标记清除算法一样首先对要清理的对象进行标记,不同的是,接下来他将存活的对象往一端移动,然后回收该端边界以外的内存空间。
    4、 分代收集算法(Generational Collection),现在不少虚拟机采用该算法,如:HotSpot,该算法把对象按不同的存活周期将内存瓜分为几个区域。如:HotSpot把堆分为新生代与老年代,然后根据各个区域的特点采用比较适合的收集算法。一般新生代对象生命周期比较短,所以采用复制算法;而老年代对象生命周期较长,所以比较适合采用标记-清理、标记-整理算法来回收。

文章首发地址:Solinx

http://www.solinx.co/archives/45

时间: 2024-08-06 20:01:09

JVM探索之——内存管理(二)的相关文章

JVM探索之——内存管理(一)

本系列的第一篇文章,预计本系列最后面会有两三个案例. Java与C.C++不一样Java不需要Coder进行手动内存管理,而这一切都交给JVM进行自动内存管理,这从某种程度上来说也减轻了我们Coder不少的编码量,而我们是否还有必要了解JVM的内存管理机制呢,答案是否定的:因为Java也会和C.C++一样发生内存泄漏.内存溢出,尽管它发生这些事故会少很多,但一旦发生了而你又不了解他的内存管理机制这将是非常棘手的问题:还有个原因就是Java是运行在JVM上的,而不能JVM参数可能会影响到程序的执行

JVM探索之内存管理(三)

上节我们介绍了JVM垃圾回收的原则,还有几个垃圾收集算法:标记-清除算法.复制算法.标记整理算法.分代收集算法:现在将要说HotSpt的垃圾收集器,这小节将只是理论. Java虚拟机规范对垃圾收集器的具体实现并没有任何规定,所以不同厂商.不同版本的虚拟机提供的垃圾收集器会有很大的不同.下面所介绍的收集器只是HotSpt1.7的垃圾收集器.     HotSpot堆的瓜分 HotSpt它把内存空间分为几个区域:新生代.老年代.永久代:上节中也说到了分代垃圾收集算法的主要思想按对象的生命周期来进行分

JVM学习笔记-内存管理

第一章 内存分配 1. 内存区域. 方法区和堆(线程共享),程序计数器 , VM栈 和 本地方法栈(线程隔离). 1) java虚拟机栈:线程私有.描述的是java方法执行的内存模型:栈帧,用户存储 局部变量表,操作数栈,动态链接,方法出口等信息. 局部变量表在编译时即可完全确定!如果线程请求的栈深度大于 规定的深度,StackOverflowError. 2) 本地方法栈,类似. 3)堆:垃圾收集器管理的主要区域.线程共享. 4)方法区: 各个线程共享.存储:加载的类信息,常量,静态变量,即时

JVM内存管理(二)

JVM内存管理 JVM在执行java程序的过程中,会把内存划分为若干个不同的数据区域.这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则依赖用户线程的启动和结束而建立和销毁. 程序计数器 程序计数器:当前线程所执行字节码的行号指示器. 由于JVM的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器只会执行一条线程中的指令.为了线程切换后能够恢复到正确的执行位置,每条线程都需要一个独立的程序计数器,各线程之间计数器

linux内核探索之内存管理(二):linux系统中的内存组织--结点、内存域和页帧

本文主要参考<深入linux内核架构>(3.2节)及Linux3.18.3内核源码 概述:本文主要描述了内存管理相关的数据结构:结点pg_data_t.内存域struct zone以及页帧(物理页):struct page ,以及该结构相关的一些基本概念. 1. 概述 内存划分为接点,每个结点关联到系统中的一个处理器,在内核中表示为pg_data_t. 各个结点又划分为内存域,比如DMA内存域,高端内存域,普通内存域. 内核内存域的宏: enum zone_type { #ifdef CONF

OC内存管理(二)

一:autorelease 1> autorelease原理:将我们创建的对象放到一个对象释放池中(是一个栈区)当池子释放时,会将池子中的对象都做一次release操作(自动释放池存放在一个池子中,就近原则,符合先进后出) 2>自动释放池的创建方式 (1)ios 5.0以前的创建方式 NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init]; ````````````````` [pool  release];//[pool drain]

操作系统概念学习笔记 16 内存管理(二) 段页

操作系统概念学习笔记 16 内存管理 (二) 分页(paging) 分页(paging)内存管理方案允许进程的物理地址空间可以使非连续的.分页避免了将不同大小的内存块匹配到交换空间上(前面叙述的内存管理方案都有这个问题,当位于内存中的代码和数据需要换出时,必须现在备份存储上找到空间,这是问题就产生了.备份存储也有前面所述的与内存相关的碎片问题,只不过访问更慢). 传统上,分页支持一直是由硬件来处理的.最近的设计是通过将硬件和操作系统相配合来实现分页. 基本方法 实现分页的基本方法设计将物理内存分

Objective-C学习笔记_内存管理(二)

一.属性的内部实现原理 assign的属性内部实现 setter方法: // setter方法 @property (nonatomic, assign) NSString *name; - (void)setName:(NSString *)name { _name = name; } getter方法: // getter方法 - (NSString *)name { return _name; } 观察下面代码会出现什么问题? NSString *name = [[NSString all

linux内核探索之内存管理(四):对页表和页表项的操作

接上一节,主要参考<深入Linux内核架构>(3.3节),即linux-3.18.3 1. 对PTE的操作 最后一级页表中的项不仅包含了指向页的内存位置的指针,还在上述的多于比特位包含了与页有关的附加信息.尽管这些数据是特定于CPU的,它们至少提供了有关页访问控制的一些信息.下列位在linux内核支持的大多数CPU中都可以找到. arch/x86/include/asm/pgtable_types.h #define _PAGE_BIT_PRESENT 0 /* is present */ #