java c 内存管理_Java基础-JVM内存管理-内存管理

内存管理

Java的内存管理采用“自动内存管理”机制。

JDK1.7内存模型

JVM 运行时有5个区域:程序计数器(Program Counter Register)、虚拟机栈(VM Stack)、本地方法栈(Native Method Stack)、方法区(Method Area)、堆(Heap)。

58441f52f13a

JDK1.8内存模型

JVM运行时有4个区域:程序计数器(Program Counter Register)、虚拟机栈(VM Stack)、本地方法栈(Native Method Stack)、堆(Heap)。

58441f52f13a

1.8同1.7在内存模型方面的变化有:

1)方法区变化【方法区仍然是堆的一个逻辑部分】

1.8同1.7比,最大的差别就是:元数据区取代了永久代,就是JDK8没有了PermSize相关的参数配置了。

元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元数据空间并不在虚拟机中,而是使用本地内存。

方法区与永久代的区别?

方法区只是JVM规范定义,而永久代为具体的实现,元空间也是方法区在jdk1.8中的一种实现。

为什么废除永久代?

官方文档:移除永久代是为融合HotSpot JVM与 JRockit VM而做出的努力,因为JRockit没有永久代,不需要配置永久代。

解释:PermGen很难调整,PermGen中类的元数据信息在每次FullGC的时候可能被收集,但成绩很难令人满意。而且应该为PermGen分配多大的空间很难确定,因为PermSize的大小依赖于很多因素,比如JVM加载的class总数,常量池的大小,方法的大小等。并且永久代内存经常不够用发生内存泄露。

总结:永久代分配空间难确定,且容易引发内存泄漏,为了融合 JRockit VM 的优点将永久代废除。

2)运行时常量池变化【运行时常量池仍然是方法区的一部分】

在近三个JDK版本(1.6、1.7、1.8)中, 运行时常量池(Runtime Constant Pool)的所处区域一直在不断的变化,在JDK1.6时它是方法区的一部分;1.7又把他放到了堆内存中;1.8之后出现了元空间,它又回到了方法区。

内存区域以及介绍

内存空间

线程共享

存储的数据类型

说明

异常

配置

程序计数器

线程私有

1字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。

2在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程上次运行到哪儿了。

1是一块较小的存储空间

2线程私有。每条线程都有一个程序计数器。

3是唯一一个不会出现OutOfMemoryError的内存区域。

4生命周期随着线程的创建而创建,随着线程的结束而死亡

不会抛出OutOfMemoryError异常。

Java栈

线程私有

局部变量表(存放基本数据类型变量、引用类型的变量、returnAddress类型的变量。)

操作数栈

动态链接

方法出口信息

虚拟机栈描述的是Java方法执行的内存模型: 每个方法被执行时会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息.

每个方法被调用至返回的过程, 就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程(VM提供了-Xss来指定线程的最大栈空间, 该参数也直接决定了函数调用的最大深度).

会抛出StackOverFlowError和OutOfMemoryError异常。

StackOverflowError:线程请求的栈深度大于虚拟机所允许的最大深度;OutOfMemoryError:虚拟机在扩展栈时无法申请足够的内存空间

58441f52f13a

-Xss

本地方法栈

线程私有

本地方法的: 局部变量表(存放基本数据类型变量、引用类型的变量、returnAddress类型的变量。) 操作数栈 动态链接 方法出口信息

与Java Stack作用类似, 区别是Java Stack为执行Java方法服务, 而本地方法栈则为Native方法服务, 如果一个VM实现使用C-linkage模型来支持Native调用, 那么该栈将会是一个C栈, 但HotSpot VM直接就把本地方法栈和虚拟机栈合二为一.

会抛出StackOverFlowError和OutOfMemoryError异常。

StackOverflowError:线程请求的栈深度大于虚拟机所允许的最大深度;OutOfMemoryError:虚拟机在扩展栈时无法申请足够的内存空间

58441f52f13a

-Xss

Java堆

线程共享

所有new出的对象,对象的引用放在栈中

堆是用来存放对象的内存空间。

几乎所有的对象都存储在堆中。

OutOfMemoryError

堆的大小既可以固定也可以扩展,但主流的虚拟机堆的大小是可扩展的(通过-Xmx和-Xms 控制),因此当线程请求分配内存,但堆已满,且内存已满无法再扩展时,就抛出OutOfMemoryError。

58441f52f13a

-Xms -Xmx -Xmn

方法区

线程共享

已被虚拟机加载的类信息 常量 静态变量 即时编译器编译后的代码等数据

1线程共享 方法区是堆的一个逻辑部分,因此和堆一样,都是线程共享的。整个虚拟机中只有一个方法区。

2永久代 方法区中的信息一般需要长期存在,而且它又是堆的逻辑分区,因此用堆的划分方法,我们把方法区称为老年代。

3内存回收效率低 方法区中的信息一般需要长期存在,回收一遍内存之后可能只有少量信息无效。对方法区的内存回收的主要目标是:对常量池的回收 和 对类型的卸载。

4Java虚拟机规范对方法区的要求比较宽松。 和堆一样,允许固定大小,也允许可扩展的大小,还允许不实现垃圾回收。

JDK7及其之前版本

OutOfMemoryError

根据Java 虚拟机规范的规定,当方法区无法满足内存分配需求时,将抛出OutOfMemoryError 异常。

58441f52f13a

-XX:PermSize -XX:MaxPermSize

JVM相关异常

58441f52f13a

JVM内存的异常有两种,分别是栈溢出和内存溢出。

栈溢出是StackOverflowError,对应虚拟机栈和本地方法栈,当线程请求的栈深度大于虚拟机所允许的深度时就会抛出该异常。

内存溢出是OutOfMemoryError,一般对应线程共享区域,如堆和元数据区。当内存不足以分配对象空间,而堆或方法区又无法扩展时,就会抛出该异常。

比如对应堆区的OutOfMemoryError: Java heap space,对应元数据区的OutOfMemoryError: Metaspace。如果Java虚拟机栈容量可以动态扩展 ,当栈扩展时无法申请到足够的内存也会抛出OutOfMemoryError。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值