java 内存管理机制

运行时数据区域

java 虚拟机在运行java程序时会把内存分为不同的数据区域。有的随着进程启动存在,有的随着线程启动而建立和销毁。大致可下图表示:

java-runtime-memory.jpg

灰色部分:线程共享;

白色部分:线程隔离区域; 

程序计数器 Program Counter Register

由于java虚拟机是多线程轮流获取cpu时间片来得到机会执行代码,任何一个时刻,一个处理器都会去执行一条指令,为了切换线程能恢复之前的执行位置,就需要进行记录。

作用:标记正在执行的字节码指令的地址(针对java方法,如果是native方法则不记录)。

Java虚拟机栈

它的生命周期和线程一样,描述的是java方法执行的内存模型,每个方法在执行的时候会创建一个栈帧,用于存储局部变量表,操作数栈、方法出口等信息。每个方法从调用到执行完成的过程对应一个栈帧在虚拟机栈的入栈到出栈的过程。

局部变量表存放了编译器各种基本数据类型、引用类型,它所需的内存空间在编译期间就完成分配了。

本地方法栈

和虚拟机栈很类似,本地方法栈是描述native方法。二者都会抛出Stackoverflow 和OutOfMemory 两种异常。

  • StackOverflowError: 线程请求的栈深度大于虚拟机所允许的最大深度(循环递归)
  • OutOfMemoryError: 虚拟机在扩展栈是无法申请到足够的内存空间,一般可以通过不停地创建线程引起此种情况

Java堆

被线程共享区域。主要存放对象实例,并且分配内存。java堆也是GC的主要区域,因此垃圾回收也称为 GC堆。回收的算法采用分代收集算法(新生代,老生代)。java堆在磁盘上是不连续的,可以固定大小,也可以扩展,当无法扩展时,就会抛出OOM异常,Android中经常谈的。

扫描二维码关注公众号,回复: 5694369 查看本文章

方法区

线程共享区域。存储被虚拟机加载的类信息,常量、静态变量、即时编译器编译的代码。少量的GC也发生在这部分区域。方法区有一个运行时常量池,它是存放类的版本、字段、方法、接口、以及符号引用信息。

直接内存

这部分不是虚拟机运行时数据区的一部分,也不是java虚拟机规范中定义的内存区域。是jdk1.4版本加入的。知道这些就行就了。

对象创建

虚拟机遇到new 指令时,会先检查这个类的符号引用,以及符号引用的类是否被加载。然后再通过指针碰撞或空闲列表的方式区分配内存。(java堆内存是否规整,一边是分配区域,一边是空闲区域,还是说对象内存区域随便划分)哪种方式由java堆是否规整,是否规整有GC收集器是否带有压缩整理功能决定的。当内存分配完后,进行赋值和初始化操作。

对象的访问定位

   

句柄访问

直接指针访问

这里的Reference包含了对象的引用信息。java虚拟机采用的是第二种方式,其实这里我们可以想象一下java把指针给封装起来了,底层思想仍然是一致的。

内存溢出分析

了解到java运行时内存分配就是要去分析问题,而最主要的问题就是内存溢出。它的由来就是物理机器再运行程序时会分配其最大内存,这些内存被计数器、虚拟机栈、本地方法栈、方法区、堆瓜分,然后各司其职,当内存不够用的时候就会产生OOM。最多的情况还是方法区和堆上面。

堆溢出示例 :

// 不停的添加实例对象,运行一段时间程序会报错
        while(true){
            list.add(new People());
        }

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space,注意 Java heap space 这里指明了哪个空间异常了。

猜你喜欢

转载自blog.csdn.net/sjh_389510506/article/details/88806347