热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

JVM学习总结体系结构、内存管理和垃圾回收

这片其实是一个总结帖,并非完全原创,里面包含很多文章链接,不喜误入!!!jvm内容较多从虚拟机
这片其实是一个总结帖,并非完全原创,里面包含很多文章链接,不喜误入!!!

jvm内容较多从虚拟机装载到类加载器、类执行、内存分配、垃圾回收,有很多的知识需要理解。这里主要集中内存管理和垃圾回收。更多虚拟机原理参考下面两篇博客。

jvm原理:http://blog.csdn.net/witsmakemen/article/details/28600127

jvm原理和优化:http://blog.csdn.net/ning109314/article/details/10411495/


前言

java的jvm不只有sun一家,我们使用的sun的jvm为HotSpot,可以用命令java -version看一下

C:\Users\Administrator>java -version
java version "1.8.0_91"
Java(TM) SE Runtime Environment (build 1.8.0_91-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.91-b14, mixed mode)


另外两种为
OracleJRockit,
IBM JVM。如果有更多请在下面回复。订制自己的虚拟机恐怕也只有Oracle和IBM这种大公司有这种实力。以下的jvm都是以HotSpot JVM为例。


三大虚拟机垃圾回收机制的比较http://www.tuicool.com/articles/JVRzAj



一、java虚拟机体系结构

JVM可以由不同的厂商来实现。由于厂商的不同必然导致JVM在实现上的一些不同,然而JVM还是可以实现跨平台的特性,这就要归功于设计JVM时的体系结构了。一个JVM实例的行为不光是它自己的事,还涉及到它的子系统、存储区域、数据类型和指令这些部分,它们描述了JVM的一个抽象的内部体系结构,其目的不光规定实现JVM时它内部的体系结构,更重要的是提供了一种方式,用于严格定义实现时的外部行为。每个JVM都有两种机制,一个是装载具有合适名称的类(类或是接口),叫做类装载子系统;另外的一个负责执行包含在已装载的类或接口中的指令,叫做运行引擎。每个JVM又包括方法区、堆、Java栈、程序计数器和本地方法栈这五个部分。

图-JVM的体系结构

关于体系结构的详述请参考:

1.java虚拟机深入研究http://www.kuqin.com/java/20080525/8907.html

2.百度百科一下jvm

但是当你读完上面的深入研究对jvm体系结构了解多少呢?最核心的运行数据区域即java虚拟机的堆,这里面才是内存管理、垃圾回收需要做的。java虚拟机的内存管理和垃圾回收其是一回事,以下就只用垃圾回收这个词了。


关于java jvm的栈和堆

上图中java中的栈(stack)是由java管理,推(heap)是交给程序(员)的。

1.当前线程的栈存储基本变量和堆里的对象引用(指针),基本类型和引用的对象分别存于不同的内存区域,一个是栈一个是堆。

String str = new String("abc");

String str1 = "abc";

int aa = 1;

解释:aa是基本类型存于栈内;str是对象,他的引用存于栈内,str这个对象存于堆中;str1又是一个基本类型(虽然他是string但他不是new出来的)存于栈内。

补充1:栈内的基本类型和对象的引用如果相同,那么栈的指针都指向同一个栈内的区域(值)。改变变量出现不同,栈的指针发生变化指向其他区域或者分配内存并指向新分配的内存区域。

补充2:vm(virtual machine)不是vs(virtual server),虚拟机像真正的机器一样管理着堆和栈,堆是可以动态分配内存的,而栈的内存大小是固定的。所以jvm中堆和非堆(并不是栈)都可以指派最小和最大内存。



jvm体系结构中的运行时数据区(runtime data area)


1.Java堆(Java Heap)

对于大多数应用来说,Java 堆是Java 虚拟机所管理的内存中最大的一块。Java 堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。

Java堆中唯一的目的就是
存放对象实例,几乎所有的对象实例都在这里分配内存。

这一点在Java 虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配①,但是随着JIT 编译器的发展与逃逸分析技术的逐渐成熟,栈上分配、标量替换②优化技术将会导致一些微妙的变化发生,所有的对象都分配在堆上也渐渐变得不是那么“绝对”了。

Java堆是垃圾收集器管理的主要区域,因此很多时候也被称作“GC堆”(Garbage Collected Heap)。

根据Java 虚拟机规范的规定,Java 堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可,就像我们的磁盘空间一样。在实现时,既可以实现成固定大小的,也可以是可扩展的,不过当前主流的虚拟机都是按照可扩展来实现的(通过-Xmx和-Xms 控制)。如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError 异常



java虚拟机内存:http://blog.csdn.net/taohuaxinmu123/article/details/24472073

栈和堆的区别:http://android.blog.51cto.com/268543/50100/


2.方法区(又叫Non-heap)

参考文章:

JVM学习笔记-方法区(Method Area):http://denverj.iteye.com/blog/1209506

JVM使用-XX:PermSize设置非堆内存初始值,默认是物理内存的1/64;由XX:MaxPermSize设置最大非堆内存的大小,默认是物理内存的1/4。

-XX:MaxPermSize为最大非堆内存。

补充:-Xmx的值和-XX:MaxPermSize的总和不可超过JVM内存的最大限制,-Xms和-XX:MaxPermSize一般设置为相同。

-Xms128M
-Xmx512M
-XX:PermSize=64M
-XX:MaxPermSize=128M

更多jvm参数,比如GC策略,不是这篇文章陈述内容。


2.1. 运行时常量池(Runtime Constant Pool)



    运行时常量池是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译期生成的各种字面量和符号引用,这部分内容在类加载后存放到方法区的运行时常量池中。




3.java stacks

每一个JVM线程均有一个私有的栈,与线程是同时创建的。一个JVM的栈中存放了很多frames,JVM的栈和C语言中的栈的概念很类似,它是用来存放本地变量和partial结果的(partial results),并且在方法调用和返回时起到一定作用。由于对栈的操作只有push和pop,所以可以用堆分配。JVM的栈在内存中不要求连续存放。
JVM的规范中允许栈可以是固定尺寸的,也可以根据需要动态扩展或收缩其尺寸。开发或者用户可以设定栈的初始尺寸,对于动态栈空间的情况,也可以设定栈的最大和最小值。
下面是与栈操作的一些相关异常:
1. 如果一个线程所需的栈空间大于允许值,则抛出StackOverFlowError;
2. 如果一个线程的栈可以动态扩展,但当需要扩展栈空间时发现内存空间不足;或者在初始化栈空间时,就发现内存不足了,则抛出OutOfMemoryError;

java虚拟机内存:http://blog.csdn.net/taohuaxinmu123/article/details/24472073

补充:如前面java的栈和堆提到的,java stacks中存放基本数据类型和引用。



4.PC Register(Program Counter Register)

即程序计数器寄存器
JVM支持同一时间同时运行多个线程,每一个线程都有它们自己的pc register。在同一个时刻,JVM的线程只能运行一个单独方法中的代码,此方法称为该线程的当前方法(Current Method)。 如果这个当前方法不是native的,PC register就指向正在被执行的JVM指令的地址。而如果一个当前方法是native的,则pc register中的值是不确定的。Pc register有足够的空间来存储returnAddress或者native指针。



5.本地方法栈(native method stack)

本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务。虚拟机规范中对本地方法栈中的方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。甚至有的虚拟机(譬如Sun HotSpot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。与虚拟机栈一样,本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError异常。

JVM学习笔记-本地方法栈:http://denverj.iteye.com/blog/1220969

补充:本地方法栈相当于java栈的扩展,java可能需要调用其他接口、其他语言接口,这时候就需要本地方法栈。



java jvm GC(垃圾回收)

有了jvm体系结构的认识,那么jvm的GC其实就是管理内存,回收java堆(heap)中的对象。我们都知道java所有的GC都不需要程序员显示的执行,因为在分配内存的时候jvm会根据实际环境(jvm参数)有若干种GC收集器来调用。

java的jvm包括以下四种GC收集器:Serial收集器(client级别默认的GC)、Parallel(并行)收集器(server级别默认采用的GC),CMS收集器、G1收集器

关于收集器请参考:

垃圾收集器Serial 、Parallel、CMS、G1:http://www.zicheng.net/article/55.htm



图-jvm的内存分配


上面的图并不是全部,实际是存在于年轻世代young Generation,年老世代Tenured Generation,永久世代Permanent Generation




 1.Young Generation(年轻代)


年轻代主要存放新创建的对象,内存大小相对会比较小,垃圾回收会比较频繁。年轻代分成1个Eden Space和2个Suvivor Space(命名为A和B)。当对象在堆创建时,将进入年轻代的Eden Space。垃圾回收器进行垃圾回收时,扫描Eden Space和A Suvivor Space,如果对象仍然存活,则复制到B Suvivor Space,如果B Suvivor Space已经满,则复制到Old Gen。同时,在扫描Suvivor Space时,如果对象已经经过了几次的扫描仍然存活,JVM认为其为一个持久化对象,则将其移到Old Gen。扫描完毕后,JVM将Eden Space和A Suvivor Space清空,然后交换A和B的角色(即下次垃圾回收时会扫描Eden Space和BSuvivor Space。这么做主要是为了减少内存碎片的产生。


我们可以看到:Young Gen垃圾回收时,采用将存活对象复制到到空的Suvivor Space的方式来确保尽量不存在内存碎片,采用空间换时间的方式来加速内存中不再被持有的对象尽快能够得到回收。


2.Tenured Generation(年老代)

年老代主要存放JVM认为生命周期比较长的对象(经过几次的Young Gen的垃圾回收后仍然存在),内存大小相对会比较大,垃圾回收也相对没有那么频繁(譬如可能几个小时一次)。年老代主要采用压缩的方式来避免内存碎片(将存活对象移动到内存片的一边,也就是内存整理)。当然,有些垃圾回收器(譬如CMS垃圾回收器)出于效率的原因,可能会不进行压缩。


3.Permanent Generation(持久代)


用于存放静态文件,如今Java类、方法等。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如Hibernate等,在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。持久代大小通过-XX:MaxPermSize=进行设置。

举个例子:当在程序中生成对象时,正常对象会在年轻代中分配空间,如果是过大的对象也可能会直接在年老代生成(据观测在运行某程序时候每次会生成一个十兆的空间用收发消息,这部分内存就会直接在年老代分配)。年轻代在空间被分配完的时候就会发起内存回收,大部分内存会被回收,一部分幸存的内存会被拷贝至Survivor的from区,经过多次回收以后如果from区内存也分配完毕,就会也发生内存回收然后将剩余的对象拷贝至to区。等到to区也满的时候,就会再次发生内存回收然后把幸存的对象拷贝至年老区。



总结

通常我们说的JVM内存回收总是在指堆内存回收,确实只有堆中的内容是动态申请分配的,所以以上对象的年轻代和年老代都是指的JVM的Heap空间,而持久代则是之前提到的MethodArea,不属于Heap。


对于Minor GC 和 Full GC的解释:
 
     新生代 GC(Minor GC):指发生在新生代的垃圾收集动作,因为 Java 对象大多都具
    备朝生夕灭的特性,所以 Minor GC 非常频繁,一般回收速度也比较快。

     老年代 GC(Major GC  / Full GC):指发生在老年代的 GC,出现了 Major GC,经常
    会伴随至少一次的 Minor GC(但非绝对的,在 ParallelScavenge 收集器的收集策略里
    就有直接进行 Major GC 的策略选择过程) 。MajorGC 的速度一般会比 Minor GC 慢 10
    倍以上。

虚拟机给每个对象定义了一个对象年龄(Age)计数器。如果对象在 Eden 出生并经过第一次 Minor GC 后仍然存活,并且能被 Survivor 容纳的话,将被移动到 Survivor 空间中,并将对象年龄设为 1。对象在 Survivor 区中每熬过一次 Minor GC,年龄就增加 1 岁,当它的年龄增加到一定程度(默认为 15 岁)时,就会被晋升到老年代中。对象晋升老年代的年龄阈值,可以通过参数 -XX:MaxTenuringThreshold 来设置。


至此内存分配、垃圾回收总结完了。

问题,之前只是基础知识,有更多需要了解和动手尝试:

1.什么时候jvm调用GC去执行垃圾回收?

2.哪一种垃圾回收机制更好?

3.jvm的参数需要手动设定吗,如何设定更好?

4.jvm的栈(stacks)动态分配大小吗?



推荐阅读
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • 360SRC安全应急响应:从漏洞提交到修复的全过程
    本文详细介绍了360SRC平台处理一起关键安全事件的过程,涵盖从漏洞提交、验证、排查到最终修复的各个环节。通过这一案例,展示了360在安全应急响应方面的专业能力和严谨态度。 ... [详细]
  • 在维护公司项目时,发现按下手机的某个物理按键后会激活相应的服务,并在屏幕上模拟点击特定坐标点。本文详细介绍了如何使用ADB Shell Input命令来模拟各种输入事件,包括滑动、按键和点击等。 ... [详细]
  • 在编译BSP包过程中,遇到了一个与 'gets' 函数相关的编译错误。该问题通常发生在较新的编译环境中,由于 'gets' 函数已被弃用并视为安全漏洞。本文将详细介绍如何通过修改源代码和配置文件来解决这一问题。 ... [详细]
  • 本文详细介绍了Java中的注解功能,包括如何定义注解类型、设置注解的应用范围及生命周期,并通过具体示例展示了如何利用反射机制访问注解信息。 ... [详细]
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • 2023年京东Android面试真题解析与经验分享
    本文由一位拥有6年Android开发经验的工程师撰写,详细解析了京东面试中常见的技术问题。涵盖引用传递、Handler机制、ListView优化、多线程控制及ANR处理等核心知识点。 ... [详细]
  • 解决微信电脑版无法刷朋友圈问题:使用安卓远程投屏方案
    在工作期间想要浏览微信和朋友圈却不太方便?虽然微信电脑版目前不支持直接刷朋友圈,但通过远程投屏技术,可以轻松实现在电脑上操作安卓设备的功能。 ... [详细]
  • 本次考试于2016年10月25日上午7:50至11:15举行,主要涉及数学专题,特别是斐波那契数列的性质及其在编程中的应用。本文将详细解析考试中的题目,并提供解题思路和代码实现。 ... [详细]
  • 在 Flutter 开发过程中,开发者经常会遇到 Widget 构造函数中的可选参数 Key。对于初学者来说,理解 Key 的作用和使用场景可能是一个挑战。本文将详细探讨 Key 的概念及其应用场景,并通过实例帮助你更好地掌握这一重要工具。 ... [详细]
  • 本文介绍了一种解决二元可满足性(2-SAT)问题的方法。通过具体实例,详细解释了如何构建模型、应用算法,并提供了编程实现的细节和优化建议。 ... [详细]
  • 百度搜索结果链接提取工具 UrlGetter V1.43
    该工具专为获取百度搜索引擎的结果页面中的网址链接而设计,能够解析并转换为原始URL。通过正则表达式匹配技术,精准提取网页链接,并提供详细的使用说明和下载资源。 ... [详细]
  • Java 架构:深入理解 JDK 动态代理机制
    代理模式是 Java 中常用的设计模式之一,其核心在于代理类与委托类共享相同的接口。代理类主要用于为委托类提供预处理、过滤、转发及后处理等功能,以增强或改变原有功能的行为。 ... [详细]
  • 深入解析Java异常处理机制:异常分类与检查
    本文旨在全面介绍Java中的异常分类及其检查机制,帮助开发者更好地理解和应用异常处理策略。后续将深入探讨异常处理的相关源码。 ... [详细]
author-avatar
zj
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有