JVM初识(一)运行时内存

Android不管是5.0之前的DVM还是现在的ART,不管是java 开发的还是Kotlin开发的,其本质上都是运行在虚拟机中的,那么我们有必要了解JVM在运行时内存是什么样子的,哪些地方可能会产生OOM或者stackoverflo,以及hook点无埋点统计也是需要这里的知识点。

jvm运行时内存

JVM初识(一)运行时内存_第1张图片
现代的jvm在线程私有的空间里,虚拟机栈和本地方法栈是共用的一块内存空间。

程序计数器PC

1.程序计数器是线程私有的区域,每个线程当然得有个计数器记录当前执行到那个指令。
2占用的内存空间小,可以把它看成是当前线程所执行的字节码的行号指示器。如果线程在执行Java方法,这个计数器记录的是正在执行的虚拟机字节码指令地址;如果执行的是Native方法,这个计数器的值为空(Undefined)。
3此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域

虚拟机栈

这个区域也是我们平时口中说的堆栈的栈,关于这个块区域有如下要点:
1.属于线程私有,与线程的生命周期相同
2.每一个java方法被执行的时候,这个区域会生成一个栈帧,执行引擎每调用一个函数时,就为这个函数创建一个栈帧,并加入虚拟机栈。换个角度理解,每个函数从调用到执行结束,其实是对应一个栈帧的入栈和出栈
4.栈帧中存放的局部变量有8种基本数据类型,以及引用类型(对象的内存地址)
5.当线程请求的栈的深度超出了虚拟机栈允许的深度时,会抛出StackOverFlow的错误
6.当Java虚拟机动态扩展到无法申请足够内存时会抛出OutOfMemory的错误

本地方法栈

类似于这样:System.arraycopy()调用c里面的方法
memcpy(void *destin, void *source, unsigned n)

Java堆

我们平时说得最多,关注得最多的一个区域,new 出来的对象,就存放在这里!我们后期进行的性能优化主要针对这部分内存,GC的主战场,这个地方存放的几乎所有的对象实例和数组数据。这里我大概进行了如下概括:
1.由于现在的收集器基本上采用的都是分代收集算法,所有Java堆可以细分为:新生代和老年代。再细致分就是把新生代分为:Eden空间、From Survivor空间、To Survivor空间。下一篇ava内存模型(JMMj)专门来熟悉下这里
2.当堆无法再扩展时,会抛出OutOfMemoryError异常。

方法区

1.方法区存放的是类信息、常量、静态变量等。
2.方法区是各个线程共享区域,很容易理解,我们在写Java代码时,每个线程度可以访问同一个类的静态变量对象。由于使用反射机制的原因,虚拟机很难推测那个类信息不再使用,因此这块区域的回收很难。
3.另外,对这块区域主要是针对常量池回收,值得注意的是JDK1.7已经把常量池转移到堆里面了
4.同样,当方法区无法满足内存分配需求时,会抛出OutOfMemoryError。

虚拟机栈单独拉出来分析JVM初识(一)运行时内存_第2张图片
小结

1.分清什么是实例什么是对象。Class a= new Class();此时a叫实例,而不能说a是对象。实例(或者叫引用)在栈中对象在堆中,操作实例实际上是通过实例的指针间接操作对象。多个实例可以指向同一个对象。
2.栈中的数据和堆中的数据销毁并不是同步的。方法一旦结束,栈中的局部变量立即销毁,但是堆中对象不一定销毁。因为可能有其他变量也指向了这个对象,直到栈中没有变量指向堆中的对象时,它才销毁,而且还不是马上销毁,要等垃圾回收扫描时才可以被销毁。(这里就涉及到注意内存泄漏以及软引用、弱引用、安卓里的handler之类的)
3.以上的栈、堆、代码段、数据段等等都是相对于应用程序而言的。每一个应用程序都对应唯一的一个JVM实例,每一个JVM实例都有自己的内存区域,互不影响。并且这些内存区域是所有线程共享的。

本文参考了享学课堂的大神视频以及相关文章,加入一些自己的理解点,如果有不对的地方,欢迎指正。

你可能感兴趣的:(Android中级,java,jvm,堆栈,内存泄漏)