为什么jvm的jvm内存模型图是堆的内部结构?????大佬求解

没有更多推荐了,
不良信息举报
举报内容:
JVM内存模型和垃圾收集
举报原因:
原文地址:
原因补充:
最多只允许输入30个字
加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!博客分类:
1,JVM内存模型
1.1 jvm的内存结构
1.2 垃圾回收器和垃圾回收算法
基本回收算法
(Reference Counting)
比较古老的回收算法。原理是此对象有一个引用,即增加一个计数,删除一个引用则减少
一个计数。垃圾回收时,只用收集计数为0的对象。此算法最致命的是无法处理循环引用的问题。
(Mark-Sweep)
此算法执行分两阶段。第一阶段从引用根节点开始标记所有被引用的对象,第二阶段遍历整个堆,把未标记
的对象清除。此算法需要暂停整个应用,同时,会产生内存碎片。
复制(Copying)
算法把内存空间划为两个相等的区域,每次只使用其中一个区域。垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另外一个区域中。次算法每次只处理
正在使用中的对象,因此复制成本比较小,同时复制过去以后还能进行相应的内存整理,不过出现“碎片”问题。当然,此算法的缺点也是很明显的,就是需要两倍
内存空间。
标记-整理(Mark-Compact)
此算法结合了“标记-清除”和“复
制”两个算法的优点。也是分两阶段,第一阶段从根节点开始标记所有被引用对象,第二阶段遍历整个堆,把清除未标记对象并且把存活对象“压缩”到堆的其中一
块,按顺序排放。此算法避免了“标记-清除”的碎片问题,同时也避免了“复制”算法的空间问题。
(Incremental Collecting)
实施垃圾回收算法,即:在应用进行的同时进行垃圾回收。不知道什么原因
JDK5.0中的收集器没有使用这种算法的。
分代(Generational Collecting)
于对对象生命周期分析后得出的垃圾回收算法。把对象分为年青代、年老代、持久代,对不同生命周期的对象使用不同的算法(上述方式中的一个)进行回收。现在
的垃圾回收器(从J2SE1.2开始)都是使用此算法的。
轻代分三个区。一个Eden区,两个Survivor区。大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到Survivor区
(两个中的一个),当这个Survivor区满时,此区的存活对象将被复制到另外一个Survivor区,当这个Survivor去也满了的时候,从第一
个Survivor区复制过来的并且此时还存活的对象,将被复制“年老区(Tenured)”。需要注意,Survivor的两个区是对称的,没先后关
系,所以同一个区中可能同时存在从Eden复制过来
对象,和从前一个Survivor复制过来的对象,而复制到年老区的只有从第一个Survivor去过来的对象。而且,Survivor区总有一个是空
Tenured(年老代)
年老代存放从年轻代存活的对象。一般来说年老代存放的都是
生命期较长的对象。
Perm(持久代)
于存放静态文件,如今Java类、方法等。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如Hibernate等,
在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。持久代大小通过-XX:MaxPermSize=&N&进行设置。
GC有两种类型:Scavenge
GC和Full GC
Scavenge GC
一般情况下,当新对象生成,并且在Eden申请空间失败
时,就好触发Scavenge
GC,堆Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。
对整个堆进行整理,包括Young、Tenured和Perm。Full GC比Scavenge GC要慢,因此应该尽可能减少Full
GC。有如下原因可能导致Full GC:
Tenured被写满
Perm域被写满
System.gc()被显示调用
上一次GC之后Heap的各域分配策略动态变化
垃圾回收器的类型:
可以保证大部分工作都并发进行(应用不停止),垃圾回收只暂停很少的时间,此收集器适合对响应时间要求比较高的中、大
规模应用。使用-XX:+UseConcMarkSweepGC
发收集器主要减少年老代的暂停时间,他在应用不停止的情况下使用独立的垃圾回收线程,跟踪可达对象。在每个年老代垃圾回收周期中,在收集初期并发收集器会
对整个应用进行简短的暂停,在收集中还会再暂停一次。第二次暂停会比第一次稍长,在此过程中多个线程同时进行垃圾回收工作。
器使用处理器换来短暂的停顿时间
。在一个N个处理器的系统上,并发收集部分使用K/N
可用处理器进行回收,一般情况下1&=K&=N/4
在只有一个
处理器的主机上使用并发收集器
,设置为incremental mode
模式也可获得较短的停顿时
:由于在应用运行的同时进行垃圾回收,所以有些垃圾可能在垃圾回收进行完成时产
生,这样就造成了“Floating Garbage”,这些垃圾需要在下次垃圾回收周期时才能回收掉。所以,并发收集器一般需要20%
预留空间用于这些浮动垃圾。
Concurrent Mode Failure
:并发收集器在应
用运行时进行收集,所以需要保证堆在垃圾回收的这段时间有足够的空间供程序使用,否则,垃圾回收还未完成,堆空间先满了。这种情况下将会发生“并发模式失
败”,此时整个应用将会暂停,进行垃圾回收。
启动并发收集器
:因为并发收集在应用运行时进行收
集,所以必须保证收集完成之前有足够的内存空间供程序使用,否则会出现“Concurrent Mode Failure”。通过设置-XX:CMSInitiatingOccupancyFraction=&N&
定还有多少剩余堆时开始执行并发收集
行处理器:
--适用情况:数据量比较小(100M左右);单处理器下并且对响应时间无要求的应用。
--缺点:只能
用于小型应用
并行处理器:
--适用情况:“对吞吐量有高要求”,多CPU、对应用响
应时间无要求的中、大型应用。举例:后台处理、科学计算。
--缺点:应用响应时间可能较长
并发处理器:
适用情况:“对响应时间有高要求”,多CPU、对应用响应时间有较高要求的中、大型应用。举例:Web服务器/应用服务器、电信交换、集成开发环境。
JVM调优(young
generation=new generation
堆内存设置选项
set new generation heap size
-XX:NewSize
一般是最大堆内存的1/4,目的在增大较大数量的短生命周期对象的内存。相当于增加cpu数目,可并行的分配内存,但不可并行回收内存
set max ~同上
-XX:MaxNewSize
set new generation heap ration
-XX:SurvivorRation
iNIT:EDEN/SURVIVOR=8. THEN try
set minimu heap size
一般情况同xmx,避免垃圾回收后重新分配空间
set max heap size
一般情况同xms,避免垃圾回收后重新分配空间
set stack size
用来控制本地线程栈的大小。一般不要太大(2MB)
SET NO CLASS GC
-noclassgc
取消系统对特定类的垃圾回收;但这个类的所有引用没有后,不会被gc,进而无需重新装载;快???
3,对内存友好的编程ITEMS
很多人都觉得java编程相对CPP一个非常大的优势是不需要考虑内存,这放在入门阶段可以理解,但要想成为层次高点的coder,必须熟悉java的内
存分配机制,并能够写出对内存友好的代码。
最基本的建议就是尽早释放无用对象的引用。大多数程序员在使用临时变量的时候,都是让引用变量在退出活动域(scope)后,自动设置为null。我们在
使用这种方式时,必须特别注意一些复杂的对象图,例如数组、队列、树、图等,这些对象之间的相互引用关系较为复杂。对于这类对象,GC回收它们的效率一般
较低。如果程序允许,尽早将不用的引用对象赋为null。这样可以加速GC的工作。 例如:
A a = new A();
// 应用a对象
a = // 当使用对象a之后主动将其设置为空
注意,如果a是方法的返回值,千万不要做这样的处理,否则你从该方法中得到的返回值永远为空,而且这种错误不易被发现。因此这时很难及时抓住、排除
NullPointerException异常。
(2)尽量少用finalize函数。
finalize函数是Java给程序员提供一个释放对象或资源的机会。但是,它会加大GC的工作量,因此尽量少采用finalize方式回收资源。
大家也都知
道finalize()不好,分配代价昂贵,释放代价更昂贵(要多走一个循环,而且他们死得慢,和他们
相关联的对象也跟着死得慢了),又不确定能否被调用(JVM开始关闭时,就不会再进行垃圾收集),又不确定何时被调用(GC时间不定,即使
system.gc()也只是提醒而不是强迫GC,又不确定以什么样的顺序调用,所以finalize不是C++的析构函数,也不像C++的析构函数。
我们都知道啊,所以我从来都没使用。都是在显式的维护那些外部资源,比如在finally{}里释放。
(3)如果需要使用经常用到的图片,可以使用
soft应用类型。它可以尽可能将图片保存在内存中,供程序调用,而不引起OutOfMemory。
(4)注意集合数据类型,包括数组、树、图、
链表等数据结构,这些数据结构对GC来说,回收更为复杂。另外,注意一些全局的变量,以及一些静态变量。这些变量往往容易引起悬挂对象,造成内存浪费。
(5)尽量避免在类的默认构造器中创建、初始
化大量的对象,防止在调用其自类的构造器时造成不必要的内存资源浪费。
(6)尽量避免强制系统做垃圾内存的回收(通
过显式调用方法System.gc()),增长系统做垃圾回收的最终时间,降低系统性能。
大家都知道
System.gc()不好,full-gc浪费巨大,gc的时机把握不一定对等等,甚至有
-XX:+DisableExplicitGC的JVM参数来禁止它。
哈哈,但我还不会用System.gc()呢,不怕不怕。真的不怕吗?
查一下所用到的全部第三方类库吧...
就会老实不客气的执行System.gc()来实现分布式GC算法。但我也不会用RMI啊。那EJB呢,EJB可是建在RMI上的....
如果无可避免,用-Dsun.rmi.dgc.client.gcInterval=3600000
-Dsun.rmi.dgc.server.gcInterval=3600000 (单位为微妙)
增大大GC的间隔(原默认值为1分钟),-XX:+ExplicitGCInvokesConcurrent 让System.gc()
也CMS并发执行.
(7)尽量避免显式申请数组空间,当不得不显
式地申请数组空间时尽量准确地估计出其合理值,以免造成不必要的系统内存开销。
(8)尽量在做远程方法调用(RMI)类应用
开发时使用瞬间值(transient)变量,除非远程调用端需要获取该瞬间值(transient)变量的值。
(9)尽量在合适的场景下使用对象池技术以提
高系统性能,缩减系统内存开销,但是要注意对象池的尺寸不易过大,及时清除无效对象释放内存资源,综合考虑应用运行环境的内存资源限制,避免过高估计运行
环境所提供内存资源的数量。
java 不是有垃圾收集器了吗?怎么还泄漏啊,唬我啊??
嗯,此泄漏非比泄漏。C/C++的泄漏,是对象已不可到达,而内存又没有回收,真正的内存黑洞。
而Java的泄漏,则是因为各种原因,对象对应用已经无用,但一直被持有,一直可到达。
总结原因无外乎几方面:
生命周期极长的集合类不当持有,号称是Java内存泄漏的首因。
这些集合类的生命周期通常极长,而且是一个辅助管理性质的对象,在一个业务事务运行完后,如果没有将某个业务对象主动的从中清除的话,这个集合就会吃越来
越多内存,可以用WeakReference,如WeakHashMap,使得它持有的对象不增加对象的引用数。
定义不对,这个很简单了,方法的局部变量定义成类的变量,类的静态变量等。
常时没有加finally{}来释放某些资源,JDBC时代也是很普遍的事情。
外一些我了解不深的原因,如:Swing里的Listener没有显式remove;内部类持有外部对象的隐式引用;Finalizers造成关联对象没
有被及时清空等。
注:很多资料都是来源于网络。
浏览: 9165 次
来自: 杭州
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'jvm面试系列一:java内存模型你掌握了多少?
今天我们就来聊一聊Java内存模型,面试中面试官会通过考察你对jvm的理解更深入得了解你的水平。如果你应聘的职位涉及系统调优,如堆大小的分配、垃圾回收机制的选择、处理内存溢出、线程死锁等,对JVM这一块知识就有更高的要求。
说明:因为知识点较多,扩展开来篇幅太长,jvm系列面试题将会分解开来从内存模型,垃圾回收,类加载机制,参数调优等多个角度整理,方便大家阅读。
Java 内存模型 (Java Memory Model)
JVM中用heap堆来存储运行时数据,所有类实例和数组由堆分配内存,JVM启动时创建堆(heap memory),在堆以外的内存叫非堆(non-heap memory).
Java Memory Model
各部分所存储的内容
下面详细了解下jvm内存模型的三个组成部分:
1.堆(Heap Space)
所有new出来的对象内存都分配在堆中,堆被分成年轻代(Yong Generation)和老年代(Old Generation),新生代又分成Eden和两个Survivor区。
年轻代是所有新对象产生的地方。当年轻代内存空间被用完时,就会触发垃圾回收。这个垃圾回收叫做Minor GC。年轻代被分为3个部分——Enden区和两个Survivor区。
新对象在年轻代产生,当年轻代空间占满后,会触发Minor GC开始垃圾回收,大多数新建对象位于Eden区,Eden区满后触发一次Minor GC,存活下来的对象转到survivor0区,survivor0区满后触发执行Minor GC,survivor0区存活对象全部转入survivor1区,这样保证一段时间内总有一个survivor区为空。经过多次Minor GC仍然存活的对象会转入老年代,这是由设定的年龄阈值来决定的。
老年代空间存储长期存活的对象,老年代占满时会触发Major GC,花费时间较长,由于垃圾回收会导致“stop the world”事件,即所有线程都会停下来等待垃圾回收执行完成,所以对响应要求高的应用应尽量减少发生Major GC,如微博后台程序发生Major GC就会导致前台页面刷新超时,若是股票等实时交易应用发生Major GC导致响应超时,可能会给客户带来损失,这都是系统调优要考虑到的问题。
在分代收集算法中,由于年轻代中对象的存活率低,通常使用复制算法进行垃圾回收,老年代一般使用”标记-清理”或”标记-整理”算法回收存活率较高的对象。
永久代(Permanent Generation),用来保存程序运行时长期存活的对象,如类的元数据。
但是Java8中已经用metaspace完全替代了永久代。jvm参数-XX:PermSize 和 -XX:MaxPermSize选项会被忽略。
Java8中PermGen被Metaspace替代
3.Native Area
PC程序计数器
程序计数器用来为线程独立拥有,线互补互补影响,保证线程切换后能准确恢复到执行位置,线程执行指令的跳转、循环、分支都要依赖计数器来完成。
本地方法栈
Java栈也是线程独立的,每个线程建立一个Java栈存储数据,所以不需要关心其数据一致性,也不会存在同步锁的问题。每个Java栈存在多个栈帧(stack frame),栈帧与方法一一对应,在Hot Spot虚拟机中,使用-Xss参数来设置栈的大小,栈的大小直接决定了函数调用的可达深度。
最后结合一段代码,看看这段代码在JVM内存模型中是如何分配的:
import java.text.SimpleDateF
import java.util.D
import java.util.logging.L
* Created by on 17/4/19.
public class HelloWorld {
private static Logger LOGGER= Logger.getLogger(HelloWorld.class.getName());
public void sayHello(String message){
SimpleDateFormat formatter =new SimpleDateFormat("dd.MM.YYYY");
String today =formatter.format(new Date());
LOGGER.info(today+":"+message);
代码对应的存储位置
募才网送福利啦~~~
关注募才订阅号:muciapin,全套安卓视频免费给!回复“安卓”即可免费获取学习资源
来募才网问答社区,还有更多惊喜等着你哦。关注募才订阅号:muciapin,即可免费获取学习资源
责任编辑:
声明:本文由入驻搜狐号的作者撰写,除搜狐官方账号外,观点仅代表作者本人,不代表搜狐立场。
募才网人才专属免费职业顾问,深圳计算机行业协会人才服务中心。
大学生IT求职平台,IT人才输送中心
今日搜狐热点\ 后端开发
JVM系列分析- 内存模型
JVM的内存模型是java语言绕不开的一个话题。要进行java的性能调优,首先就要了解其内存模型。在诸多的面试笔试中,这也是很多面试官会考察的内容。
本篇文章简单介绍JVM内存模型的概念,结构和对应的参数设置,并根据具体的代码案例讲解一下内存分配情况。
1.JVM内存结构
由图可以较为清楚的看到,JVM的内存空间分为3大部分,分别是堆内存、方法区和栈内存。其中栈内存可以再细分为java虚拟机栈和本地方法栈。堆内存可以划分为新生代和老年代。新生代中还可以再次划分为Eden区、From Survivor区和To Survivor区。划分出来的各个区,分别保存不同的数据,并且在一般情况下,其采取的内存回收策略也是不同的。
2.JVM内存区域功能
通过上图可以看到JVM运行时的数据区:
2.1 堆内存
堆内存是JVM内存模型中最大的一块区域,被所有线程共享,是在JVM启动时候进行创建的。几乎所有的对象的空间分配都是在堆内存上进行分配的。
考虑到JVM的内存回收机制,堆内存可以划分为新生代和老年代两个区域(默认新生代与老年代的空间大小为1:2)。新生代可以再划分为Eden区、From Survivor区和To Survivor区(三者比例为8:1:1)。几乎所有的新对象的创建都是在Eden区进行的。在垃圾回收(GC)过程中,Eden中的活跃对象会被转移到Survivor区,当再到达一定的年龄(经历过的Minor GC的次数),会被转移到老年代中。
堆可以处于物理上不连续的内存空间中,但是需要满足逻辑上的连续。在实现时,可以实现成固定大小的,也可以是可扩展的,不过当前主流的虚拟机都是按照可扩展来实现的
2.2 方法区
方法区又被成为永久代(HotSpot虚拟机的设计团队选择把GC分代收集扩展至方法区),同样也是被所有的线程共享的。该区域中主要保存类的信息、静态变量、常量和即时编译器编译后的代码等数据。
JDK1.7中,已经把放在永久代的字符串常量池移到堆中。JDK1.8撤销永久代,引入元空间。
方法区不需要连续的内存,可以选择固定大小或者可扩展。并且还可以选择不实现垃圾收集。相对而言,垃圾收集行为在这个区域是比较少出现的,但并非数据进入了方法区就如永久代的名字一样“永久”存在了。这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载,一般来说这个区域的回收“成绩”比较难以令人满意,尤其是类型的卸载,条件相当苛刻,但是这部分区域的回收确实是有必要的。当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。
2.3 程序计数器
程序计数器是用于标识当前线程执行的字节码文件的行号指示器。多线程情况下,每个线程都具有各自独立的程序计数器,所以该区域是非线程共享的内存区域。
当执行java方法时候,计数器中保存的是字节码文件的行号;当执行Native方法时,计数器的值为空。
2.4 Java栈
java栈是线程私有的内存区域,其中存储的是栈帧。对于一个java的方法,其开始调用,则会创建一个栈帧,保存到java栈中;当该方法执行完成,则对应的是出栈的过程。
如果方法methodOne方法调用了methodTwo,那么methodOne就会先入栈创建一个栈桢,接着methodTwo再入栈成为栈顶(假设没有其他的方法执行),methodTwo执行完先出栈,接着methodOne执行完出栈。
每个栈帧中,保存执行对应方法所必须的信息,主要包含局部变量表,操作栈,动态连接和方法出口等信息。
局部变量表中,可以存放的数据有8种基本数据类型(boolean,byte,char,short,int,float,long,double),对象引用和returnAddress类型。其中long和double因为是64位,会占用两个局部变量的空间。
在Java虚拟机规范中,对这个区域规定了两种异常状况:如果线程请求的栈深度大于虚拟机所允许的深度(比如递归调用的时候),将抛出StackOverflowError异常;如果虚拟机栈可以动态扩展(当前大部分的Java虚拟机都可动态扩展,只不过Java虚拟机规范中也允许固定长度的虚拟机栈),当扩展时无法申请到足够的内存时会抛出OutOfMemoryError异常。
2.5 本地方法栈
本地方法栈也是线程私有的内存区域,与java栈比较相似,不同之处在于该区域主要是保存Native方法相关的数据。Native方法是非Java语言编写的方法。
与虚拟机栈一样,本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError异常。
3.JVM内存参数设置
-Xms设置堆的最小空间大小。
-Xmx设置堆的最大空间大小。
-XX:NewSize设置新生代最小空间大小。
-XX:MaxNewSize设置新生代最大空间大小。
-XX:PermSize设置永久代最小空间大小。
-XX:MaxPermSize设置永久代最大空间大小。
-Xss设置每个线程的堆栈大小。
4.代码案例讲解
使用如下的一段简单代码案例,说明一下各个内存区域中保存的信息。
堆中进行对象的空间分配,比如Hashtable对象和String对象。
方法中保存类信息(TestJVM),方法(put方法,print方法,test方法)和静态变量(NUM)
java栈中保存对象引用(score)
5.参考资料
本文原创发布于慕课网 ,转载请注明出处,谢谢合作
若觉得本文不错,就分享一下吧!
作者的热门文章
请登录后,发表评论
评论加载中...
Copyright (C) 2018 imooc.com All Rights Reserved | 京ICP备 号-11jvm的内存模型?怎么栈溢出,堆溢出?gc?真的用到过没_百度知道
jvm的内存模型?怎么栈溢出,堆溢出?gc?真的用到过没
我有更好的答案
但是在平常编程中,肯定经常遇到OOM问题。jvm的内存模型,简单来说就是堆栈结构,实际上内存的划分很复杂。推荐《深入java虚拟机》来入门了解。栈溢出和堆溢出,顾名思义,就是栈和堆上的内存不够用了,就抛出OOM异常呗。可以通过调整参数来控制堆栈的内存大小:-Xmn -XmxGC就是针对堆,栈,方法区的垃圾回收行为。这些问题还是要看书的,打字打死也说不完。
jvm的内存模型?怎么栈溢出,堆溢出?gc?真的用到过没栈是程序自动分配内存的,一般有2M,视情况而定,当超过了这一限制就会产生栈溢出,而栈是程序员分配的ls:列目录。 用法:ls或ls dirName,参数:-a显示所有文件,-l详悉列出文件。 mkdir:建目录。 用法:mkdir dirName,参数:-p建多级目录,如:mkdir a/b/c/d/e/f -p mount:挂载分区或镜像文件(.iso,.img)文件。 用法: a.磁盘分区:mount dev...
为您推荐:
其他类似问题
勃兰兑斯的相关知识
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。

我要回帖

更多关于 jvm内存模型 面试 的文章

 

随机推荐