开篇废话

今天我们一起来学习JVM的内存分配,主要目的是为我们Android内存优化打下基础。

一直在想以什么样的方式来呈现这个知识点才能让我们易于理解,最终决定使用方法为:图解+源代码分析

欢迎访问我的个人博客:senduo's blog

希望能在我们平时开发写代码的时候,能够知道当前写的这段代码,内存方面是如何分配的。

我们深知,一个Java程序员在很多时候根本不用操心内存的释放,而是依靠JVM去管理,以前写C++代码的时候,却要时刻记着new的空间要及时释放掉,不然程序很容易出现内存溢出的情况。因为,Java在这方面确实方便了许多,让我们有更多精力去考虑业务方面的实现。但是,这并不意味着我们就能肆无忌惮的使用内存,因为:

1.JVM并不会及时的去清理内存2.我们无法通过代码去控制JVM去清理内存

这就要求我们平时在开发过程中,要了解JVM的垃圾回收机制,合理安排内存。

那么怎么样才能合理安排内存呢?那么就需要我们了解JVM的内存分配机制,而后才能真正控制好,让程序运行在我们鼓掌之中。


技术详情

1.JVM内存模型

平时我们对于Java内存都有一个比较粗略的概念,就是分堆和栈,但实际上还是复杂得多,以下给出完整内存模型:

内存模型

相对应区域的内容为:

内容模型

1.1程序计数器PC

这一个区域我概括了以下几个要点:

1.这一区域不会出现OOM(Out Of Memory)错误的情况2.属于线程私有,因为每一个线程都有自己的一个程序计数器,来表示当前线程执行的字节码行号3.标识Java方法的字节码地址,而不是Native方法4.处于CPU上,我们无法直接操作这块区域

1.2虚拟机栈

这个区域也是我们平时口中说的堆栈的栈,关于这个块区域有如下要点:

1.属于线程私有,与线程的生命周期相同2.每一个java方法被执行的时候,这个区域会生成一个栈帧4.栈帧中存放的局部变量有8种基本数据类型,以及引用类型(对象的内存地址)5.java方法的运行过程就是栈帧在虚拟机栈中入栈和出栈的过程6.当线程请求的栈的深度超出了虚拟机栈允许的深度时,会抛出StackOverFlow的错误7.当Java虚拟机动态扩展到无法申请足够内存时会抛出OutOfMemory的错误

1.3本地方法栈

这个区域,属于线程私有,顾名思义,区别于虚拟机栈,这里是用来处理Native方法(Java本地方法)的,而虚拟机栈是处理Java方法的。对于Native方法,Object中就有不少的Native的方法,hashCode,wait等,这些方法的执行很多时候都是借助于操作系统。

这一区域也有可能抛出StackOverFlowError 和 OutOfMemoryError

1.4 Java堆

我们平时说得最多,关注得最多的一个区域,就是他了。我们后期进行的性能优化主要针对这部分内存,GC的主战场,这个地方存放的几乎所有的对象实例和数组数据。这里我大概进行了如下概括:

1.Java堆属于线程共享区域,所有的线程共享这一块内存区域2.从内存回收角度,Java堆可被分为新生代和老年代,这样分能够更快的回收内存3.从内存分配角度,Java堆可划分出线程私有的分配缓存区(Thread Local Allocation Buffer,TLAB),这样能够更快的分配内存4.当Java虚拟机动态扩展到无法申请足够内存时会抛出OutOfMemory的错误

1.5 方法区

方法区主要存放的是已被虚拟机加载的类信息、常量、静态变量、编译器编译后的代码等数据。GC在该区域出现的比较少。概括如下:

1.方法区属于线程共享区域2.习惯性加他永久代3.垃圾回收很少光顾这个区域,不过也是需要回收的,主要针对常量池回收,类型卸载4.常量池用于存放编译期生成的各种字节码和符号引用,常量池具有一定的动态性,里面可以存放编译期生成的常量5.运行期间的常量也可以添加进入常量池中,比如string的intern()方法。

1.6 运行时常量池

运行时常量池也是方法区的一部分,用于存放编译器生成的各种字面量和符号引用。单独拿出来说明一下,是因为我们平时使用String比价多,涉及到这一块的知识,但这一块区域不会抛出OutOfMemoryError

2.JVM内存源码示例说明

首先写了一个main方法,来做演示,代码如下:

package senduo.com.memory.allocate;/*** ****************************************************************** * 文件作者:ouyangshengduo* * 创建时间:2017/8/11* * 文件描述:内存分配调用过程演示代码* * 修改历史:2017/8/11 9:39***************************************/
public class MemoryAllocateDemo {public static void main(String[] args){ //JVM自动寻找main方法/*** 执行第一句代码,创建一个Test实例test,在栈中分配一块内存,存放一个指向堆区实例对象的指针*/Test test = new Test();/*** 执行第二句代码,声明定义一个int型变量(8种基本数据类型),在栈区直接分配一块内存存储这个变量的值*/int date = 9;/*** 执行第三句代码,创建一个BirthDate实例bd1,在栈中分配一块内存,存放一个指向堆区实例对象的指针*/BirthDate bd1 = new BirthDate(13,6,1991);/*** 执行第四句代码,创建一个BirthDate实例bd2,在栈中分配一块内存,存放一个指向堆区实例对象的指针*/BirthDate bd2 = new BirthDate(30,4,1991);/*** 执行第五句代码,方法test1入栈帧,执行完出栈*/test.test1(date);/*** 执行第六句代码,方法test2入栈帧,执行完出栈*/test.test2(bd1);/*** 执行第七句代码,方法test3入栈帧,执行完出栈*/test.test3(bd2);}
}

演示过程一

1.JVM自动寻找main方法,执行第一句代码,创建一个Test类的实例test,在栈中分配一块内存,存放一个指向堆区对象的指针110925。2.创建一个int型的变量date,由于是基本类型,直接在栈中存放date对应的值9。3.创建两个BirthDate类的实例bd1、bd2,在栈中分别存放了对应的指针指向各自的对象,他们在实例化时调用了有参数的构造方法,因此对象中有自定义初始值。

图解如下:

内存分配调用演示一

演示过程二

1.test1方法入栈帧,以date为参数2.value为局部变量,把value放在栈中,并且把date的值赋值给value3.把123456赋值给value局部变量4.test1方法执行完,value内存被释放,test1方法出栈

内存分配调用演示二

内存分配调用演示二

内存分配调用演示二

演示过程三

1.test2方法入栈帧,以实例bd1为参数2.birthDate为局部变量,把birthDate放在栈中,把bd1的引用的值赋值给birthDate,也就是bd1与birthDate的地址都是指向同一个堆区的实例
3.在堆区new了一个对象,并且把这个堆区的指针保存在栈区中birthDate对应的内存空间,这个时候,bd1与birthDate指向了不同的堆区,那么birthDate的改变,并不会对bd1造成影响4.test2方法执行完,栈中的birthDate空间被释放,test2方法出栈,但堆区的内存空间则要等待系统自动回收

内存分配调用演示三

内存分配调用演示三

内存分配调用演示三

演示过程四

1.test3方法入栈帧,以实例bd2为参数2.birthDate为局部变量,把birthDate放在栈中,把bd2的引用的值赋值给birthDate,也就是bd2与birthDate的地址都是指向同一个堆区的实例
3.调用birthDate的setDay方法,因为birthDate与bd2指向的是同一个对象,也就是bd2调用了setDay方法,所以,也会bd2造成影响4.test3方法执行完,栈中的birthDate空间被释放,test3方法出栈

内存分配调用演示四

内存分配调用演示四

内存分配调用演示四

3.JVM内存分配小结

跟着上面四个步骤,走一遍,会发现其实也不会那么复杂,掌握思想就能摸到门路了,我们平时注意区分一下基本数据类型的变量和引用数据类型变量,以下进行了几点概括:

1.局部变量中的基本数据类型的值直接存栈中2.局部变量中的引用数据类型在栈中存的是引用类型的指针(地址)3.栈中的数据与堆中的数据内存回收并不是同步的,栈中的只要方法运行完,就会直接销毁局部变量,但堆中的对象不一定立即销毁4.类的成员变量在不同对象中各不相同,都有自己的存储空间(成员变量在堆中的对象中)。而类的方法却是该类的所有对象共享的,只有一套,对象使用方法的时候方法才被压入栈,方法不使用则不占用内存

干货总结

终于把JVM内存分配的分享写完了,一路写下来,确实对内存分配又深入了解了一次。期间参考了以下博客:

Java之美[从菜鸟到高手演变]之JVM内存管理及垃圾回收

Java 内存分配全面浅析

Jvm内存模型

通过对JVM内存模型的认识后,下一章将进行JVM垃圾回收机制的探索。

    本文转自 一点点征服   博客园博客,原文链接http://www.cnblogs.com/ldq2016/p/8036464.html:,如需转载请自行联系原作者

Android性能调优篇之探索JVM内存分配相关推荐

  1. Android性能调优篇之探索垃圾回收机制

    开篇废话 如果我们想要进行内存优化的工作,还是需要了解一下,但这一块的知识属于纯理论的,有可能看起来会有点枯燥,我尽量把这一篇的内容按照一定的逻辑来走一遍.首先,我们为什么要学习垃圾回收的机制,我大概 ...

  2. JVM性能调优3_垃圾收集器与内存分配策略__享学课堂

    Stop The World现象 GC收集器和我们GC调优的目标就是尽可能的减少STW的时间和次数. 内存分配与回收策略 对象优先在Eden分配,如果说Eden内存空间不足,就会发生Minor GC ...

  3. JVM性能调优2_垃圾收集器与内存分配策略__享学课堂

    判断对象的存活 引用计数法: 优点:快,方便,实现简单: 缺点:对象相互引用时,很难判断对象是否该回收. 可达性分析: 这个算法的基本思路就是通过一系列的称为"GC Roots"的 ...

  4. jvm性能调优 - 05对象在JVM内存中的分配和流转

    文章目录 前文回顾 大部分正常对象都优先在新生代分配内存 到底什么情况下会触发新生代的垃圾回收? 长期存活的对象会躲过多次垃圾回收? 老年代会垃圾回收吗? 关于新生代和老年代的对象分配,这就完了吗? ...

  5. Mr. Cappuccino的第19杯咖啡——金三银四面试题之JVM性能调优篇

    金三银四面试题之JVM性能调优篇 1. 什么是Java类加载器? 2. 读取class文件的来源有哪些? 3. 谈谈类加载器加载的流程? 4. 类加载器有哪几种? 5. 哪些操作会初始化类加载器? 6 ...

  6. “金三银四”春招指南之“性能调优”:MySQL+Tomcat+JVM,看完还怕面试官的轰炸?

    春招指南之"性能调优":MySQL+Tomcat+JVM,还怕面试官的轰炸? 01 MySQL性能调优 1.1 MySQL性能调优问题有哪些?怎么学? 关于这个,给大家看一份学习大 ...

  7. android性能调优的工具,神兵利器-Android 性能调优工具 Hugo

    在进行Android性能调优.减少应用卡顿时,寻找可优化的code是一个必要的过程.如何发现应用中的耗时任务甚至是耗时函数呢,如果可以在log中打印每个方法的执行时间,甚至把执行方法时的输入输出同时打 ...

  8. 发布即巅峰:Java性能调优六大工具:MAT内存分析工具

    MAT内存分析工具 MAT是MemoryAnalyzerTool的简称,它是一款功能强大的Java堆内存分析器,可以用于查找内存泄漏以及查看内存消耗情况.MAT是 基于Eclipse开发的一款免费的性 ...

  9. gateway 内存溢出问题_带你学习jvm java虚拟机 arthas/性能调优/故障排除/gc回收/内存溢出等...

    学完本课程,您将掌握: 内存溢出问题实战 CPU飙升问题实战 阿里巴巴Arthas在线诊断 Class字节详细拆解 手写类加载器.四种类加载器.双亲委托模型 对象创建.存储.访问.加载解析 性能调优. ...

最新文章

  1. python 常量_python学习丨变量与常量
  2. java 设置光标_java光标位置怎么设置 java设置光标位置方法
  3. ITK:仅将过滤器应用于图像的指定区域
  4. 最常见的显示设置和快捷键
  5. VBA操作word生成sql语句
  6. ODT .NET 详解之 SqlDataSource 访问 Oracle
  7. android优雅的一个侧滑
  8. SQL Server数据库占用过多内存的解决方法
  9. oracle 基础查询语句
  10. C++ 前置操作符与后置操作符
  11. CMPP3.0实现物联网卡通讯
  12. 鼠标模拟android,android_模拟鼠标指针demo
  13. API接口设计的五大公共参数
  14. excel数据解析内存占用优化
  15. vue 路由跳转打开新窗口(被浏览器拦截)
  16. 微信小程序之短信验证码
  17. 6-4 选队长 (10 分)
  18. See You Again
  19. RISC-V 实现整数运算指令(Part 1)
  20. Android百度鹰眼轨迹

热门文章

  1. 输入和输出移位寄存器的同步串行模式
  2. 从头开始学JavaScript (五)——操作符(二)
  3. [Android] 获取设备的APP信息
  4. Bitmap的一个简单实现
  5. 相对熵/KL散度(Kullback–Leibler divergence,KLD)
  6. windows安装配置git和Tortoisegit
  7. 【裴蜀定理】BZOJ 1441 MIN
  8. MySQL启动关闭添加到 /etc/init.d/mysqld
  9. org.quartz-scheduler 基础过程
  10. NOIP2008传纸条[DP]