资讯专栏INFORMATION COLUMN

Java GC

justCoding / 530人阅读

摘要:对字节码文件进行解释执行,把字节码翻译成相关平台上的机器指令。使用命令可对字节码文件以及配置文件进行打包可对一个由多个字节码文件和配置文件等资源文件构成的项目进行打包。和不存在永久代这种说法。

Java技术体系

从广义上讲,Clojure、JRuby、Groovy等运行于Java虚拟机上的语言及其相关的程序都属于Java技术体系中的一员。如果仅从传统意义上来看,Sun官方所定义的Java技术体系包括以下几个组成部分:

Java程序设计语言

各种硬件平台上的Java虚拟机

Class文件格式

Java API类库

来自商业机构和开源社区的第三方Java类库

我们可以把Java程序设计语言、Java虚拟机、Java API类库这三部分统称为JDK(Java Development Kit),JDK是用于支持Java程序开发的最小环境。

另外,可以把Java API类库中的Java SE API子集[1]和Java虚拟机这两部分统称为JRE(Java Runtime Environment),JRE是支持Java程序运行的标准环境。

以上是根据各个组成部分的功能来进行划分的,如果按照技术所服务的领域来划分,或者说按照Java技术关注的重点业务领域来划分,Java技术体系可以分为4个平台,分别为:

Java Card:支持一些Java小程序(Applets)运行在小内存设备(如智能卡)上的平台。

Java ME(Micro Edition):支持Java程序运行在移动终端(手机、PDA)上的平台,对Java API有所精简,并加入了针对移动终端的支持,这个版本以前称为J2ME。

Java SE(Standard Edition):支持面向桌面级应用(如Windows下的应用程序)的Java平台,提供了完整的Java核心API,这个版本以前称为J2SE。

Java EE(Enterprise Edition):支持使用多层架构的企业应用(如ERP、CRM应用)的Java平台,除了提供Java SE API外,还对其做了大量的扩充[3]并提供了相关的部署支持,这个版本以前称为J2EE。

JVM

Java Virtual Machine,Java虚拟机。是java编译后的.class文件(字节码文件)与硬件系统之间的接口,也就是说用来运行.class文件。JVM实现了Java最重要的特性:平台无关性

编译后的 Java 程序指令并不直接在硬件系统的 CPU 上执行,而是由 JVM 执行,JVM屏蔽了与具体平台相关的信息。JVM对字节码文件进行解释执行,把字节码翻译成相关平台上的机器指令。

javac 是收录于 JDK 中的 Java 语言编译器。我们使用javac命令编译Java源文件,得到.class文件。然后使用java命令执行.class文件(也就是使用JVM运行.class文件)。使用jar命令可对字节码文件以及配置文件进行打包(可对一个由多个字节码文件和配置文件等资源文件构成的项目进行打包)。

所以不熟悉java的同学要注意了,JVM并不是用来编译和执行java的,JVM只负责执行字节码文件,编译java文件由javac来完成

java有一套公用的规范:Java Language and Virtual Machine Specifications

目前有三大Java虚拟机:HotSpot,oracle JRockit,IBM J9。

JRockit是oracle发明的,用于其WebLogic服务器,IBM JVM是IBM发明的用于其Websphere服务器,不同的JDK可能存在兼容性问题。

JRockit和J9不存在永久代这种说法。这里只讨论HotSpot虚拟机,这也是目前使用的最多的JVM。Sun JDK7 HotSpot虚拟机的内存模型如下图所示:

JVM的内存可分为:线程栈、堆、静态方法区,native方法使用的是直接内存,不包含在JVM中。Java NDK可以调用C/C++。

方法区

方法区是可供各线程共享的运行时内存区域

方法区(Method Area)与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做Non-Heap(非堆),目的应该是与Java堆区分开来。

在不同的JDK版本中,方法区中存储的数据是不一样的。在JDK1.6及之前,运行时常量池是方法区的一个部分,同时方法区里面存储了类的元数据信息、静态变量、即时编译器编译后的代码(比如spring 使用IOC或者AOP创建bean时,或者使用cglib,反射的形式动态生成class信息等)等。在JDK1.7及以后,JVM已经将运行时常量池从方法区中移了出来,在JVM堆开辟了一块区域存放常量池。

方法区和永久代的关系

方法区(method area)只是JVM规范中定义的一个概念,用于存储类信息、常量池、静态变量、JIT编译后的代码等数据,具体放在哪里,不同的实现可以放在不同的地方。

永久代是Hotspot虚拟机特有的概念,是方法区的一种实现,别的JVM都没有这个东西。在Java 8中,永久代被彻底移除,取而代之的是另一块与堆不相连的本地内存——元空间(Metaspace),‑XX:MaxPermSize 参数失去了意义,取而代之的是-XX:MaxMetaspaceSize。

永久代(PermGen)包含了JVM需要的应用元数据,这些元数据描述了在应用里使用的类和方法。注意,永久代不是Java堆内存的一部分。永久代存放JVM运行时使用的类。永久代同样包含了Java SE库的类和方法。永久代的对象在full GC时进行垃圾收集。

对于习惯在HotSpot虚拟机上开发、部署程序的开发者来说,很多人都更愿意把方法区称为“永久代”(Permanent Generation),本质上两者并不等价,仅仅是因为HotSpot虚拟机的设计团队选择把GC分代收集扩展至方法区,或者说使用永久代来实现方法区而已,这样HotSpot的垃圾收集器可以像管理Java堆一样管理这部分内存,能够省去专门为方法区编写内存管理代码的工作。对于其他虚拟机(如BEA JRockit、IBM J9等)来说是不存在永久代的概念的。原则上,如何实现方法区属于虚拟机实现细节,不受虚拟机规范约束,不同的JVM厂商,针对自己的JVM可能有不同的方法区实现方式。但使用永久代来实现方法区,现在看来并不是一个好主意,因为这样更容易遇到内存溢出问题(永久代有-XX:MaxPermSize的上限,J9和JRockit只要没有触碰到进程可用内存的上限,例如32位系统中的4GB,就不会出现问题),而且有极少数方法(例如String.intern())会因这个原因导致不同虚拟机下有不同的表现。因此,对于HotSpot虚拟机,根据官方发布的路线图信息,现在也有放弃永久代并逐步改为采用Native Memory来实现方法区的规划了,在目前已经发布的JDK 1.7的HotSpot中,已经把原本放在永久代的字符串常量池移出。

也就是说永久代是JVM规范中方法区的一种实现方式

Java虚拟机规范对方法区的限制非常宽松,除了和Java堆一样不需要连续的内存和可以选择固定大小或者可扩展外,还可以选择不实现垃圾收集。相对而言,垃圾收集行为在这个区域是比较少出现的,但并非数据进入了方法区就如永久代的名字一样“永久”存在了。这区域的内存回收目标主要是针对常量池的回收和对类型的卸载,一般来说,这个区域的回收“成绩”比较难以令人满意,尤其是类型的卸载,条件相当苛刻,但是这部分区域的回收确实是必要的。在Sun公司的BUG列表中,曾出现过的若干个严重的BUG就是由于低版本的HotSpot虚拟机对此区域未完全回收而导致内存泄漏。

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

堆大小=新生代+老年代。默认情况下,新生代(Young generation)占$frac{1}{3}$的堆空间大小,老年代(Old generation)占$frac{2}{3}$的堆空间大小。

新生代被细分为一个Eden(伊甸园)和两个Survivor区域,这两个Survivor区域分别被命名为fromto以示区分。默认情况下,它们的空间大小关系是Eden:from:to=8:1:1

JVM每次只会使用Eden和其中一块Survivor区域来为对象服务,所以无论什么时候,总有一块Survivor区域空闲着。因此,新生代实际上可以用的内存空间为90%的新生代空间。

下面要讲的GC(garbage collection)就是针对进行内存回收。

GC的策略

分区的目的是为了方便对不同的区采取不同的回收策略。GC分为两种:新生代中的Minor GC,老生代中的Major GC;两个加在一起叫做Full GC,由于Major GC的前提是Minor GC,所以发生Major GC就一定代表着Full GC,。新生代是GC收集垃圾的频繁区域。

数据会首先分配到Eden区中(当然也有特殊情况,如果是大对象那么会直接放到老年代(大对象是指需要大量连续内存的java对象)),当Eden没有足够的空间的时候就会触发JVM发起一次Minor GC。如果对象经历一次Minor GC还存活,并且又能被Survivor空间接受,那么将被移动到Survivor空间当中,并将其年龄设为1,对象在Survivor中每熬过一次Minor GC,年龄就加1,当年龄达到一定的程度(默认为15)时,就被晋升到老年代中了,当然晋升老年代的年龄是可以设置的。新生代是GC收集垃圾的频繁区域。

新生代主要存放的是那些很快就会被GC回收掉的或者不是特别大的对象(要看你是否设置了-XX:PretenureSizeThreshold参数了)。-XX:PretenureSizeThreshold 的默认值和作用

-Xms2000m -Xmx2000m -Xmn800m -XX:PermSize=64m -XX:MaxPermSize=256m -Xss1M Xms是JVM初始堆的大小,Xmx是JVM最大堆大小,Xmn是新生代大小,PermSize是永久代的初始大小,MaxPermSize是永久代的最大大小,Xss是每个线程栈的大小。JVM系列三:JVM参数设置、分析

Xms64m or -Xms64M

Xmx1g or -Xmx1G

Can also use 2048MB to specify 2GB

Also, make sure you just use whole numbers when specifying your arguments. Using -Xmx512m is a valid option, but -Xmx0.5g will cause an error.

虚拟机初始化时已经设定了各部分的内存大小,分为三部分:

新生代:新创建的对象

老年代:经过多次垃圾回收没有被回收的对象或者太大的对象

永久代:JVM自身使用的内存,包含类信息等

新生代策略

新生代采用复制算法。将新生代分为3个区:较大的Eden和两个较小的Survivor。发生在新生代的GC为Minor GC,在Minor GC时会将新生代中还存活着的对象复制进一个Survivor中,然后对Eden和另一个Survivor进行清理。所以平常可用的新生代大小为Eden大小+一个Survivor大小。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/72658.html

相关文章

  • [译]GC专家系列2:Java 垃圾回收的监控

    摘要:原文链接这是专家系列文章的第二篇。运行在本地虚拟机上的应用的又称为,通常与相同。性能数据需要持续观察,因此在运行时需要定时输出的监控信息。新生代容量的统计信息。是提供的一个式的图表监控工具。 原文链接:http://www.cubrid.org/blog/dev-platform/how-to-monitor-java-garbage-collection/ 这是GC专家系列文章的第二...

    yiliang 评论0 收藏0
  • Java 开启 gc 日志

    摘要:启动使用或这两个参数可以创建基本的日志,使用可以创建更加详细的日志。我们可以设置日志的文件大小和数量上面设置只输出个文件,每个文件,文件的基本名字是,使用启动的时间和进程来分割日志。 构建一个 jar 包程序 使用 Spring Boot 构建一个简单的 web 程序,可以直接使用 java -jar 来启动。 @RestController @RequestMapping(/root...

    huashiou 评论0 收藏0
  • Java7的新特性

    摘要:语言特性系列的新特性的新特性的新特性的新特性的新特性的新特性的新特性的新特性的新特性序本文主要讲的新特性,相对于而言,增加了一些重要的特性,比如,不像那么鸡肋,也算是一个重要的版本。其他支持的下划线支持参考 Java语言特性系列 Java5的新特性 Java6的新特性 Java7的新特性 Java8的新特性 Java9的新特性 Java10的新特性 Java11的新特性 Java12...

    April 评论0 收藏0
  • [译]GC专家系列1:理解Java垃圾回收

    摘要:本文是成为专家系列的第一篇。然而,在多线程环境下,将会有别样的状况。在中正是通过解决了多线程问题。在最后的并发清理阶段,垃圾回收过程被真正执行。在垃圾回收执行过程中,其他线程依然在执行。 原文链接:http://www.cubrid.org/blog/de... 了解Java的垃圾回收(GC)原理能给我们带来什么好处?对于软件工程师来说,满足技术好奇心可算是一个,但重要的是理解GC能帮...

    diabloneo 评论0 收藏0
  • jvm性能优化

    摘要:前言入门垃圾回收机制后,接下来可以学习性能调优了。输出老年代空间的性能数据。新生代最小空间容量,单位。拥有者表示线程成功竞争到对象锁。线程状态,未启动的。,无限期等待另一个线程执行特定操作。主要调优参数设定堆内存大小,这是最基本的。 Java程序员进阶三条必经之路:数据库、虚拟机、异步通信。 前言 入门JVM垃圾回收机制后,接下来可以学习性能调优了。主要有两部分内容: JDK工具的使...

    WelliJhon 评论0 收藏0
  • 《深入理解Java虚拟机》(六)堆内存使用分析,垃圾收集器 GC 日志解读

    摘要:堆内存使用分析,垃圾收集器日志解读重要的东东在中,对象实例都是在堆上创建。机制是由提供,用来清理需要清除的对象,回收堆内存。在中,是由一个被称为垃圾回收器的守护线程执行的。 堆内存使用分析,垃圾收集器 GC 日志解读 重要的东东 在Java中,对象实例都是在堆上创建。一些类信息,常量,静态变量等存储在方法区。堆和方法区都是线程共享的。 GC机制是由JVM提供,用来清理需要清除的对象,...

    CODING 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<