Java虚拟机--自动内存管理机制

PS :https://blog.csdn.net/column/details/java-vm.html

Java虚拟机内存模型

这里写图片描述
在这里插入图片描述

  • 虚拟机栈:Java方法的内存模型,即每个方法的执行都会创建一个虚拟机栈帧,方法的执行过程就是栈帧的入栈出栈,每个栈帧用于存储局部变量表、操作数栈、动态链接、方法返回地址和一些额外的附加信息(运行期会有JIT优化,但我们理论上认为这部分所需内存编译期可知并写入了方法表);线程独立;StackOverflowError和OutOfMemoryError
    • 局部变量表:存储编译期可知的基本数据类型、对象引用reference、返回值类型returnAddress(它指向了一条字节码指令的地址);局部变量表的空间在java编译成class文件时就已经确定其最大容量,运行期间不会改变;局部变量表是可以复用的,当指令执行超过某变量的作用范围则该指令占有的slot(变量槽,描述每个变量占用空间的最小单位,long、double可能需要两个slot来表示)可以被重用。局部变量表的变量并不会赋予初始值,直接使用会报错。
    • 操作数栈:方法开始时为空,执行过程中将各种操作入栈出栈(即执行iadd等指令)。编译成class时确定栈大小,java虚拟机是基于操作数栈的,执行速度相对慢,但可移植性强;Android虚拟机是基于寄存器的,执行速度快,移植性差
    • 动态链接:每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态链接。
    • 方法返回地址:正常时,调用者的PC计数器的值作为返回地址(把返回值压入调用者的操作数栈中,调整PC计数器的值以指向方法调用指令后面的一条指令);异常退出则由异常处理器决定
  • 本地方法栈:native方法的内存模型,与虚拟机栈雷同。
  • Java堆:存放对象实例和数组;线程共享;Java垃圾收集器的主要对象,根据垃圾收集的需要还可分为老年代、新生代等。可能划分出多个线程私有的分配缓冲区。OutOfMemoryError
  • 方法区:存储虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等,线程共享;这个内存区可不实现垃圾回收,若实现主要回收废弃的常量(如方法区中有“abc”常量,当前系统无String对象引用“abc”即可回收)和无用类(满足下列条件:1.堆中不存在实例2.加载该类的ClassLoader被回收3.类对应的Class对象没有引用,无法通过反射加载);OutOfMemoryError
    • 运行时常量池:方法区的一部分,用于存放编译期生成的各种字面量和符号引用;
  • 程序计数器:当前线程(线程独立)所执行的虚拟机字节码的行号指示器。即正在执行的虚拟机字节码指令的地址。执行Java代码时指向字节码地址,执行Native方法则为空,不会OOM。(程序计数器的作用在于多线程,单线程无需程序计数器也可以根据字节码的跳转命令顺利执行,多线程中需要挂起恢复时才需要知道之前执行到什么位置)
  • 直接内存:非虚拟机规范内容,受本机总内存的大小及处理器寻址空间限制;OutOfMemoryError

PS:不用对象置null的意义
这个其实主要在于局部变量表的slot可以被复用机制产生。

{
	byte[] placeholder = new byte[64 * 1024 * 1024];
}
//int a = 0;  或者 placeholder = null;
System.gc();

上述代码,刚出了placeholder作用范围马上GC过来回收垃圾,此时placeholder并不会被回收,因为这块空间没有需要被复用,GC并不会回收它。而如果把注释打开,则因为需要填充新的变量,GC就会把placeholder占用的空间回收填充新的变量了。

JVM和DVM的区别?

垃圾回收机制

虚拟机栈、本地方法栈、程序计数器中的数据随线程的生命而创建回收,随方法的入栈出栈而创建回收,无需垃圾回收机制。垃圾回收机制的重点在Java堆和方法区

对象是否已经无用判断?

1.引用计数法:每个对象有一个引用计数器来记录当前引用数,为0则可回收。缺陷:很难解决对象之间互相循环引用的问题
2.可达性分析法:GC Roots枚举根节点(一个集合)没有引用链相连的对象就是不可用对象。GC Roots的集合对象包括:①虚拟机栈(栈帧中本地变量表)中引用的对象②方法区中类静态属性引用的对象③方法区中常量引用的对象④本地方法栈中JNI引用的对象

对象的回收

GC Roots不可达则标记对象;判断是否执行finalize()方法(对象没有覆盖finalize方法或finalize已经被虚拟机调用过则不执行),若需要执行,则执行后再次判断对象是否GC Roots可达,还是不可达则回收(可在finalize中与GC Roots对象关联)

GC Roots枚举根节点集合的维护

代码执行过程中有符合条件的对象都需要加入到GC Roots集合中,且获取时需要暂停整个系统,否则分析结果无法得到保障。通常,代码执行到安全点才会停下来维护GC Roots枚举(主动式中断:GC设置一个标志,各线程执行时执行到安全点会去轮询,主动挂起,维护GC Roots枚举)

垃圾回收算法

  • 标记清除算法:产生大量碎片
  • 标记整理算法:耗时,有连续空间
  • 复制算法:浪费内存空间
  • 分代收集算法:年轻代(空间较小,垃圾收集频繁而快速)、老年代(空间较大,垃圾收集较少,一般采用标记整理法,较为耗时)

JDK1.7中垃圾回收器

  • Serial (串行)收集器
    这里写图片描述
  • ParNew
    与Serial类似,只不过新生代GC线程是多线程而已
  • Parallel
    与Serial类似,只不过新生代GC线程和老年代GC线程都是多线程而已
    这里写图片描述
  • CMS (优点:并发收集、低停顿 缺点:对CPU资源非常敏感,无法处理浮动垃圾,产生大量碎片)
    ①初始标记(CMS initial mark) 标记GC Roots能直接关联到的对象;阻塞,耗时短
    ②并发标记(CMS concurrenr mark) 标记回收对象;并发
    ③重新标记(CMS remark) 标记因用户运行产生的可回收对象;阻塞
    ④并发清除(CMS concurrent sweep) 清理;并发
    这里写图片描述
  • G1
    ①初始标记(Initial Marking)
    ②并发标记(Concurrent Marking)
    ③最终标记(Final Marking)
    ④筛选回收(Live Data Counting and Evacuation)
    这里写图片描述

Java虚拟机相关配置

  • -Xmx –Xms:指定最大堆和最小堆
  • -Xmn 设置新生代大小
  • -XX:NewRatio 新生代(eden+2*s)和老年代(不包含永久区)的比值。例如:4,表示新生代:老年代=1:4,即新生代占整个堆的1/5
  • -XX:SurvivorRatio 设置两个Survivor区和eden的比值。例如:8,表示两个Survivor:eden=2:8,即一个Survivor占年轻代的1/10
  • -XX:+HeapDumpOnOutOfMemoryError OOM时导出堆到文件,根据这个文件,我们可以看到系统dump时发生了什么
  • -XX:+HeapDumpPath 导出OOM的路径
  • -XX:OnOutOfMemoryError 在OOM时,执行一个脚本。 可以在OOM时,发送邮件,甚至是重启程序
  • -XX:PermSize -XX:MaxPermSize 设置方法区的初始空间和最大空间
  • -Xss 设置栈空间的大小

成员变量存在于堆内存中,局部变量存在于栈内存中,静态变量存在于方法区中

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值