JVM内存管理概述与android内存泄露分析

Posted 一汪海洋

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM内存管理概述与android内存泄露分析相关的知识,希望对你有一定的参考价值。

 一.内存划分

将内存划分为六大部分,分别是PC寄存器、JAVA虚拟机栈、JAVA堆、方法区、运行时常量池以及本地方法栈.
1、PC寄存器(线程独有):全称是程序计数寄存器,它记载着每一个线程当前运行的JAVA方法的地址,
如果是当前执行的是本地方法,则程序计数器会是一个空地址。它的作用就是用来支持多线程,线程的阻塞、恢复、
挂起等一系列操作,直观的想象一下,要是没有记住每个线程当前运行的位置,又如何恢复呢。依据这一点,
每一个线程都有一个PC寄存器,也就是说PC寄存器是线程独有的。

2、JAVA虚拟机栈(线程独有):JAVA虚拟机栈是在创建线程的同时创建的,用于存储栈帧,
JAVA虚拟机栈也是线程独有的。

3、JAVA堆(全局共享):这一部分是JAVA内存中最重要的一部分,之所以说是最重要的一部分,
并不是因为它的重要性,而是指作为开发人员最应该关注的一部分。它随着JAVA虚拟机的启动创建,
储存着所有对象实例以及数组对象,而且内置了“自动内存管理系统”,也就是我们常说的垃圾搜集器(GC)。
JAVA堆中的内存释放是不受开发人员控制的,完全由JAVA虚拟机一手操办。对于JAVA虚拟机如何实现垃圾搜集器,
JAVA虚拟机规范没有明确的规定,也正因如此,我们平时使用的JAVA虚拟机中提供了许多种垃圾搜集器,
它们采用不同的算法以及实现方式,已满足多方面的性能需求。

4、方法区(全局共享):方法区也是堆的一个组成部分,它主要存储的是运行时常量池、字段信息、方法信息、
构造方法与普通函数的字节码内容以及一些特殊方法。它与JAVA堆的区别除了存储的信息与JAVA堆不一样之外,
最大的区别就是这一部分JAVA虚拟机规范不强制要求实现自动内存管理系统(GC)。

5、本地方法栈(线程独有):本地方法栈是一个传统的栈,它用来支持native方法的执行。
如果JAVA虚拟机是使用的其它语言实现指令集解释器的时候,也会用到本地方法栈。如果前面这两种都未发生,
也就是说如果JAVA虚拟机不依赖于本地方法栈,而且JAVA虚拟机也不支持native方法,则不需要本地方法栈。
而如果需要的话,则本地方法栈也是随每一个线程的启动而创建的。

上面五个内存区域,除了PC寄存器之外,其余四个一般情况下,都要求JAVA虚拟机实现提供给客户调节大小的参数,
也就是我们常用的Xms、Xmx等等。

Java 内存分配策略

Java 程序运行时的内存分配策略有三种,分别是静态分配,栈式分配,和堆式分配,对应的,三种存储策略使用的内存空间主要分别是静态存储区(也称方法区)、栈区和堆区。

静态存储区(方法区):主要存放静态数据、全局 static 数据和常量。这块内存在程序编译时就已经分配好,并且在程序整个运行期间都存在。 
栈区 :当方法被执行时,方法体内的局部变量都在栈上创建,并在方法执行结束时这些局部变量所持有的内存将会自动被释放。因为栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。 
堆区 : 又称动态内存分配,通常就是指在程序运行时直接 new 出来的内存。这部分内存在不使用时将会由 Java 垃圾回收器来负责回收。 
栈与堆的区别:

在方法体内定义的(局部变量)一些基本类型的变量和对象的引用变量都是在方法的栈内存中分配的。当在一段方法块中定义一个变量时,Java 就会在栈中为该变量分配内存空间,当超过该变量的作用域后,该变量也就无效了,分配给它的内存空间也将被释放掉,该内存空间可以被重新使用。

堆内存用来存放所有由 new 创建的对象(包括该对象其中的所有成员变量)和数组。在堆中分配的内存,将由 Java 垃圾回收器来自动管理。在堆中产生了一个数组或者对象后,还可以在栈中定义一个特殊的变量,这个变量的取值等于数组或者对象在堆内存中的首地址,这个特殊的变量就是我们上面说的引用变量。我们可以通过这个引用变量来访问堆中的对象或者数组。

 

什么是Java中的内存泄露

在Java中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点,首先,这些对象是可达的,即在有向图中,存在通路可以与其相连;其次,这些对象是无用的,即程序以后不会再使用这些对象。如果对象满足这两个条件,这些对象就可以判定为Java中的内存泄漏,这些对象不会被GC所回收,然而它却占用内存。

在C++中,内存泄漏的范围更大一些。有些对象被分配了内存空间,然后却不可达,由于C++中没有GC,这些内存将永远收不回来。在Java中,这些不可达的对象都由GC负责回收,因此程序员不需要考虑这部分的内存泄露。

通过分析,我们得知,对于C++,程序员需要自己管理边和顶点,而对于Java程序员只需要管理边就可以了(不需要管理顶点的释放)。通过这种方式,Java提高了编程的效率。

因此,通过以上分析,我们知道在Java中也有内存泄漏,但范围比C++要小一些。因为Java从语言上保证,任何对象都是可达的,所有的不可达对象都由GC管理。

对于程序员来说,GC基本是透明的,不可见的。虽然,我们只有几个函数可以访问GC,例如运行GC的函数System.gc(),但是根据Java语言规范定义, 该函数不保证JVM的垃圾收集器一定会执行。因为,不同的JVM实现者可能使用不同的算法管理GC。通常,GC的线程的优先级别较低。JVM调用GC的策略也有很多种,有的是内存使用到达一定程度时,GC才开始工作,也有定时执行的,有的是平缓执行GC,有的是中断式执行GC。但通常来说,我们不需要关心这些。除非在一些特定的场合,GC的执行影响应用程序的性能,例如对于基于Web的实时系统,如网络游戏等,用户不希望GC突然中断应用程序执行而进行垃圾回收,那么我们需要调整GC的参数,让GC能够通过平缓的方式释放内存,例如将垃圾回收分解为一系列的小步骤执行,Sun提供的HotSpot JVM就支持这一特性。

同样给出一个 Java 内存泄漏的典型例子,

Vector v = new Vector(10);
for (int i = 1; i < 100; i++) {
    Object o = new Object();
    v.add(o);
    o = null;   
}

在这个例子中,我们循环申请Object对象,并将所申请的对象放入一个 Vector 中,如果我们仅仅释放引用本身,那么 Vector 仍然引用该对象,所以这个对象对 GC 来说是不可回收的。因此,如果对象加入到Vector 后,还必须从 Vector 中删除,最简单的方法就是将 Vector 对象设置为 null。

 

android中常见的内存泄漏汇总

 




















以上是关于JVM内存管理概述与android内存泄露分析的主要内容,如果未能解决你的问题,请参考以下文章

JVM内存管理------JAVA语言的内存管理概述

JVM内存管理概述

Android常见内存泄露分析及MAT与Profiler内存分析时间

Android--内存泄露分析

Android--内存泄露分析

Android--内存泄露分析