JDK8androidAndroid开发经验谈

这些是你需要知道的Android内存基础

2016-10-27  本文已影响1076人  CoorChice

背景介绍

走进内存模型

Java内存层级

JVM运行时内存划分

;

JVM运行时内存划分-详细

;

程序计数器

程序计数器是线程私有的内存区域,这个区域是Java虚拟机中唯一一个没有限制OutOfMemoryError的内存区域。之所以需要它是因为Java的多线程机制是通过轮流切换分配处理器执行时间来实现的,所以会涉及到线程的暂停和重启,而在一个线程中如果正在执行Java方法的话,这个计数器就回去记录当前正在执行的虚拟机字节码,一旦被暂停,恢复只需要从程序计数器记录的为止继续执行就可以。

但是,如果线程中执行的是一个Native方法,那么程序计数器是不会去记录的,所以此时的程序计数器为空。

虚拟机栈Stack

Java虚拟机栈也是线程私有的。一条线程启动就会为它建立一个虚拟机栈。

在线程中每有一个Java方法被调用就会创建一个 “栈帧” 。每个 “栈帧” 会保存执行该方法所需的局部变量表(一般Java程序员喜欢用这个部分来代表栈)、操作数栈、动态链接以及方法出口等信息。

如果一个线程中有过多的 “栈帧” 要入到虚拟机栈中,即短时间内调用了过多的方法,就会造成 -- 栈益处 -- ,即 StackOverflowError 错误。

在这个内存区域中,如果虚拟机需要扩展内存,但没有申请到足够的内存,就会抛出 OutOfMemoryError 错误。

本地方法栈

和虚拟机栈有些类似,但它是为Native方法提供服务的。

Java堆Heap

Java的堆内存是Java虚拟机所管理的内存中最大的一块。它是所有线程所共享的,用于存放对象实例和数组,Java虚拟机的GC主要就发生在这个地方。因此这块区域也叫做"GC堆"。

Java堆的内存可以按照垃圾回收算法【分代回收】分为【新生代区】和【老年区】,进一步的,【新生代区】可以分为【Eden区】、【From Survivor区】和【To Survivor区】。

从内存角度来说,Java堆内存又被划分为线程共享的内存区域和每个线程私有的内存区域。

在Java堆区域中,如果没有内存分配给要创建的实例,并且堆也不能够再扩展,就会抛出OutOfMemoryError错误。

回收算法

Java8之后,Heap Segment真正意义上的是由Young GeneriationOld Generiation组成的。对象在其中是标记复制算法来判定一个对象是否应该被清理掉。
Heap Segment中发生的GC称为Major GC,只会影响Heap Segment区。

内存图

Young Generiation中的GC变化 — 复制算法

这个区域发生的GC称为Minor GC

Old Segment中的GC变化

这个区域发送的GC成为Full GC

方法区

Java的方法区和Java的堆内存一样是被线程所共有的。它主要存放虚拟机加载的类信息、常量、静态变量、即时编译产生的代码等。

一些地方会将方法区合并到Java堆中一起去说。把它作为“永久代”。这在Hot-Spot虚拟机而言成立,但是一般来说是不成立的。

Java的方法区如果内存不够分配的话,也是会抛出OutOfMemoryError错误的。也就是如果加载过多类到方法区的话,可能会造成方法区内存益处。

对象的可到达性

在GC检查对象的是否可以回收时,是根据对象是否可到达引用练顶端的GC Roots对象来判断的。GC Roots对象一般是虚拟机栈中变量表中引用的对象、类静态属性引用的对象、常量对象、JNI传到底层的对象。就是说,一个对象如果溯源不到这几种类型的对象的话,就认为它是无法到达的,那么它将会在GC时被回收。

新的引用类型

在JDK 1.2之后,Java扩充了4种引用类型定义:

强应用类型

即我们平时通过new关键字创建出来的的对象的引用,只要强引用还存在,那么这些对象就一定不回被回收,即使时抛出OutOfMemoryError。什么时候强引用会不存在呢?当一个方法执行完,栈帧中的变量表将会被清理,在该方法中创建使用的临时强引用就会被清理掉,之后,原本它指向的对象就被变的不可到达。

软引用类型

用来描述一些有用但不是必须的对象,即通过SoftReference创建的对象,它们将会在原本确定要发生内存溢出前的一次GC中被回收,如果回收完内存还是不够,Java堆就会抛出OutOfMemoryError错误。就是说,在触发内存溢出发生前,这些对象是和强引用一样,只要引用还在,就不会被回收。

弱引用类型

用来描述一些不必须的对象,即通过WeakReference创建的对象。弱引用对象的生命周期只有一次GC。

虚引用类型

一个对象的存在与否完全不受虚引用的影响,它唯一的用处就是可以用来监测一个对象是否被回收。

方法区中的-运行时常量池

运行时常量池主要存放类中编译时期生成的常量,当然也可以动态的往里面添加。

比如:

"abc".intern();

这个方法首先会检查运行时常量池中是否有这个字符串,有的话取出来用,没有的话生成一个并存到常量池中。

再比如,运行过程中生成通过static修饰的String时,也会加入到常量池中。对于String而言,常量 + 常量 生成的也是常量,但是常量 + 变量 生成的就是变量了。

关于Dalvik虚拟机

Dalvik虚拟机是Google按照JVM虚拟机规范定制的虚拟机,它更符合移动设备的环境要求。与标准虚拟机不同:

关于ART虚拟机

ART虚拟机在Android 5.0以后是被默认开启的,此时Dalvik已经被Google放弃维护了。它与Dalvik虚拟机的不同:

总结

看到这里的童鞋快奖励自己一口辣条吧!

想要看CoorChice更多的文章,可以加个关注哦!

上一篇下一篇

猜你喜欢

热点阅读