1. 内存模型以及分区

JVM内存模型如下图所示:
此处我们集中注意中间绿色的部分,该部分为JVM的运行时内存,该部分包含了:
线程私有的(灰色):
  1. 程序计数器:记录执行到第几条指令
  2. 虚拟机方法栈:执行Java方法所用,每执行一个方法便加入一个栈帧,里面含有局部变量表、操作栈、动态链接和方法出口等
  3. 本地方法栈:与虚拟机方法栈相似,用于执行native方法
线程共享的(蓝色):
  1. 堆:对象实例存放地,,分为年轻代和老年代。年轻代又细分为伊甸园区和两个相对的Survival区
  2. 方法区:也叫永久代,存放了类的信息、常量、类静态变量等元素,含有一个运行时常量池
 

2. 堆里面的分区:Eden,survival from to,老年代,各自的特点

1.Eden区

Eden区位于Java堆的年轻代,是新对象分配内存的地方,由于堆是所有线程共享的,因此在堆上分配内存需要加锁。而Sun JDK为提升效率,会为每个新建的线程在Eden上分配一块独立的空间由该线程独享,这块空间称为TLAB(Thread Local Allocation Buffer)。在TLAB上分配内存不需要加锁,因此JVM在给线程中的对象分配内存时会尽量在TLAB上分配。如果对象过大或TLAB用完,则仍然在堆上进行分配。如果Eden区内存也用完了,则会进行一次Minor GC(young GC)。
 

2.Survival from to

Survival区与Eden区相同都在Java堆的年轻代。Survival区有两块,一块称为from区,另一块为to区,这两个区是相对的,在发生一次Minor GC后,from区就会和to区互换。在发生Minor GC时,Eden区和Survival from区会把一些仍然存活的对象复制进Survival to区,并清除内存。Survival to区会把一些存活得足够旧的对象移至年老代。
 

3.年老代

年老代里存放的都是存活时间较久的,大小较大的对象,因此年老代使用标记整理算法。当年老代容量满的时候,会触发一次Major GC(full GC),回收年老代和年轻代中不再被使用的对象资源。
 
 
ADD:4.能说明minor gc/full gc的触发条件、OOM的触发条件,降低GC的调优的策略。 
       分析:列举一些我期望的回答:eden满了minor gc,升到老年代的对象大于老年代剩余空间full gc,或者小于时被HandlePromotionFailure参数强制full gc;gc与非gc时间耗时超过了GCTimeRatio的限制引发OOM,调优诸如通过NewRatio控制新生代老年代比例,通过MaxTenuringThreshold控制进入老年前生存次数等……

3. 对象创建方法,对象的内存分配,对象的访问定位

对象的创建

Java对象的创建大致上有以下几个步骤:
  1. 类加载检查:检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有,那必须先执行相应的类的加载过程
  2. 为对象分配内存:对象所需内存的大小在类加载完成后便完全确定,为对象分配空间的任务等同于把一块确定大小的内存从Java堆中划分出来。由于堆被线程共享,因此此过程需要进行同步处理(分配在TLAB上不需要同步)
  3. 内存空间初始化:虚拟机将分配到的内存空间都初始化为零值(不包括对象头),内存空间初始化保证了对象的实例字段在Java代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。
  4. 对象设置:JVM对对象头进行必要的设置,保存一些对象的信息(指明是哪个类的实例,哈希码,GC年龄等)
  5. init:执行完上面的4个步骤后,对JVM来说对象已经创建完毕了,但对于Java程序来说,我们还需要对对象进行一些必要的初始化。

对象的内存分配

Java对象的内存分配有两种情况,由Java堆是否规整来决定(Java堆是否规整由所采用的垃圾收集器是否带有压缩整理功能决定):
  1. 指针碰撞(Bump the pointer):如果Java堆中的内存是规整的,所有用过的内存都放在一边,空闲的内存放在另一边,中间放着一个指针作为分界点的指示器,分配内存也就是把指针向空闲空间那边移动一段与内存大小相等的距离
  2. 空闲列表(Free List):如果Java堆中的内存不是规整的,已使用的内存和空闲的内存相互交错,就没有办法简单的进行指针碰撞了。虚拟机必须维护一张列表,记录哪些内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录

对象的访问定位

对象的访问形式取决于虚拟机的实现,目前主流的访问方式有使用句柄和直接指针两种:

使用句柄:

如果使用句柄访问,Java堆中将会划分出一块内存来作为句柄池,引用中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息:
优势:引用中存储的是稳定的句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中的实例数据指针,而引用本身不需要修改。

直接指针:

如果使用直接指针访问对象,那么对象的实例数据中就包含一个指向对象类型数据的指针,引用中存的直接就是对象的地址:
优势:速度更快,节省了一次指针定位的时间开销,积少成多的效应非常可观。
 

4. GC的两种判定方法:引用计数与引用链

基于引用计数与基于引用链这两大类别的自动内存管理方式最大的不同之处在于:前者只需要局部信息,而后者需要全局信息
 

引用计数

引用计数顾名思义,就是记录下一个对象被引用指向的次数。引用计数方式最基本的形态就是让每个被管理的对象与一个引用计数器关联在一起,该计数器记录着该对象当前被引用的次数,每当创建一个新的引用指向该对象时其计数器就加1,每当指向该对象的引用失效时计数器就减1。当该计数器的值降到0就认为对象死亡。每个计数器只记录了其对应对象的局部信息——被引用的次数,而没有(也不需要)一份全局的对象图的生死信息。由于只维护局部信息,所以不需要扫描全局对象图就可以识别并释放死对象;但也因为缺乏全局对象图信息,所以无法处理循环引用的状况。
 

引用链

引用链需要内存的全局信息,当使用引用链进行GC时,从对象图的“根”(GC Root,必然是活的引用,包括栈中的引用,类静态属性的引用,常量的引用,JNI的引用等)出发扫描出去,基于引用的可到达性算法来判断对象的生死。这使得对象的生死状态能批量的被识别出来,然后批量释放死对象。引用链不需要显式维护对象的引用计数,只在GC使用可达性算法遍历全局信息的时候判断对象是否被引用,是否存活。
 

5. GC的三种收集方法:标记清除、标记整理、复制算法的原理与特点,分别用在什么地方

标记清除

标记清除算法分两步执行:
  1. 暂停用户线程,通过GC Root使用可达性算法标记存活对象
  2. 清除未被标记的垃圾对象
标记清除算法缺点如下:
  1. 效率较低,需要暂停用户线程
  2. 清除垃圾对象后内存空间不连续,存在较多内存碎片
标记算法如今使用的较少了
 

复制算法

复制算法也分两步执行,在复制算法中一般会有至少两片的内存空间(一片是活动空间,里面含有各种对象,另一片是空闲空间,里面是空的):
  1. 暂停用户线程,标记活动空间的存活对象
  2. 把活动空间的存活对象复制到空闲空间去,清除活动空间
复制算法相比标记清除算法,优势在于其垃圾回收后的内存是连续的。
但是复制算法的缺点也很明显:
  1. 需要浪费一定的内存作为空闲空间
  2. 如果对象的存活率很高,则需要复制大量存活对象,导致效率低下
复制算法一般用于年轻代的Minor GC,主要是因为年轻代的大部分对象存活率都较低
 

标记整理

标记整理算法是标记清除算法的改进,分为标记、整理两步:
  1. 暂停用户线程,标记所有存活对象
  2. 移动所有存活对象,按内存地址次序一次排列,回收末端对象以后的内存空间
标记整理算法与标记清除算法相比,整理出的内存是连续的;而与复制算法相比,不需要多片内存空间。
然而标记整理算法的第二步整理过程较为麻烦,需要整理存活对象的引用地址,理论上来说效率要低于复制算法。
因此标记整理算法一般引用于老年代的Major GC

JVM常见问题 一(转载)的更多相关文章

  1. Java虚拟机详解----JVM常见问题总结

    [声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...

  2. JVM常见问题分析

    JVM常见问题分析 启动,并且去查看日志 ./startup.sh && tail -f ../logs/catalina.out 常见有有以下几个问题: 1.java.lang.Ou ...

  3. JVM 垃圾回收- 转载 知识碎片

    最近关注了一下垃圾回收的问题,想了解一下JVM 关于方法区的垃圾回收机制,找了几篇文章,不同的文章从不同角度讲述了一下,嗯... 拼凑起来 记录一下, 有些未验证正确性... JVM 方法区 当JVM ...

  4. J2EE,J2SE,J2ME,JDK,SDK,JRE,JVM区别(转载)

    转载地址:http://blog.csdn.net/alspwx/article/details/20799017 一.J2EE.J2SE.J2ME区别 J2EE——全称Java 2 Enterpri ...

  5. 转:Jmeter常见问题 (转载) http://www.51testing.com/?uid-128005-action-viewspace-itemid-84094

    说明:这些问答是从网上转载的,自己修改了其中的一些内容,如果大家兴趣,可以将大家在使用Jmeter的时候碰到的问题写下来,我们一起补充到这个问答里面,共同努力完善jmeter的资料. 1.  JMet ...

  6. JVM常见问题(二)

    6. GC收集器有哪些?它们的特点是? 常见的GC收集器如下图所示,连线代表可搭配使用: 1.Serial收集器(串行收集器) 用于新生代的单线程收集器,收集时需要暂停所有工作线程(Stop the ...

  7. Jmeter(二十五)常见问题(转载)

    转载自 http://www.cnblogs.com/yangxia-test 收集工作中JMeter遇到的各种问题   1.  JMeter的工作原理是什么? 向服务器提交请求:从服务器取回请求返回 ...

  8. ESP8266常见问题汇总——转载自官网

    ESP8266 常见问题 本页面收集esp8266常见问题 概述 本文档主要介绍开发者在ESP8266开发中常见的一些问题. 这些问题主要包括以下几大类: 基本概念相关 ESP8266 相关 AiCl ...

  9. Hive SQL 常见问题(转载)

    http://www.aboutyun.com/thread-14942-1-1.html 问题导读 1.Hive查询语句和SQL查询语句区别与联系. 2.distribute by.group by ...

随机推荐

  1. 以太坊挖矿源码:clique算法

    上文我们总结了以太坊最主要的共识算法:ethash算法,本文将重点分析以太坊的另一个共识算法:clique. 关键字:clique,共识算法,puppeth,以太坊地址原理,区块校验,认证结点,POA ...

  2. kubernetes入门(09)kubernetes的命令

    Help执行<kubectl>或<kubectl help> | <kubectl --help>获得命令的帮助信息.kubectl的帮助信息.示例相当详细,而且简 ...

  3. 判断ssh远程命令是否执行结束

    注:这是一个没什么鸟用的功能.不过也算是一种拓展. 通常在那些"一键化部署"的shell脚本中,可能需要使用ssh执行远程命令来实现一些简单的自动化,这些远程命令可能需要执行一段时 ...

  4. MySql查询正在进行中的事务

    用法 SELECT * FROM information_schema.INNODB_TRX 这个只能查询此刻正在进行中的事务,已经完成的是查不到的 表字段定义 The INFORMATION_SCH ...

  5. ord()与char()

    >>> ord('王')#获取字符编码 29579 >>> chr(29579)#把编码转成对应的字符 '王'

  6. Java基础之关键字,标识符,变量

    Java基础 首先,来看一下Java基础知识图解,以下便是在java学习中我们需要学习设计到的一些知识(当然不是很完全). 这些都是接下来在以后的学习中我们会学到的一些知识. 1 关键字 首次先来学习 ...

  7. 使用WSUS离线下载补丁并安装在非联网的windows系统中(以Windows Server 2008 r2为例)

    首先我失去https://serverfault.com/questions/322938/finding-and-downloading-all-available-win2008-r2-and-w ...

  8. h5视频和音频 -2018/04/16

    HTML5 规定了一种通过 video 元素来包含视频的标准方法. 当前video元素支持的三种视频格式: (1)Ogg 带有Theora视频编码和Vorbis音频编码的ogg文件 (2)MPEG4带 ...

  9. mysql如何选择合适的数据类型1:CHAR与VARCHAR

    CHAR和VARCHAR类型类似,都用来存储字符串,但它们"保存"和"检索"的方式不同.CHAR属于"固定长度"的字符串,而VARCHAR属 ...

  10. react源代码重点难点分析

    网上已经有不少react源码分析文档,但都是分析主流程和主要功能函数,没有一个是从reactDOM.render()入口开始分析源码把流程走通尤其是把复杂重要的细节环节走通直到把组件template编 ...