jvm perm区什么时间jvm 垃圾回收机制

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
您的访问请求被拒绝 403 Forbidden - ITeye技术社区
您的访问请求被拒绝
亲爱的会员,您的IP地址所在网段被ITeye拒绝服务,这可能是以下两种情况导致:
一、您所在的网段内有网络爬虫大量抓取ITeye网页,为保证其他人流畅的访问ITeye,该网段被ITeye拒绝
二、您通过某个代理服务器访问ITeye网站,该代理服务器被网络爬虫利用,大量抓取ITeye网页
请您点击按钮解除封锁&JVM内存模型以及垃圾回收 - 沙翁 - 博客园
向昨天要经验;
向今天要结果;
向明天要动力
JAVA堆的描述如下:
&  内存由Perm和Heap组成。其中Heap = {Old + NEW = { Eden , from, to } }
  JVM内存模型中分两大块:
  NEW Generation:程序新创建的对象都是从新生代分配内存,新生代由Eden Space和两块相同大小的Survivor Space(通常又称S0和S1或From和To)构成,可通过-Xmn参数来指定新生代的大小,也可以通过-XX:SurvivorRation来调整Eden Space及Survivor Space的大小。垃圾回收一般用Copying的算法,速度快。每次GC的时候,存活下来的对象首先由Eden拷贝到某个Survivor Space, 当Survivor Space空间满了后, 剩下的live对象就被直接拷贝到Old Generation中去。因此,每次GC后,Eden内存块会被清空。
  Old Generation:用于存放经过多次新生代GC任然存活的对象和应用程序中生命周期长的内存对象,例如缓存对象,新建的对象也有可能直接进入老年代,主要有两种情况:①大对象,可通过启动参数设置-XX:PretenureSizeThreshold=1024(单位为字节,默认为0)来代表超过多大时就不在新生代分配,而是直接在老年代分配。②大的数组对象,切数组中无引用外部对象。老年代所占的内存大小为-Xmx对应的值减去-Xmn对应的值。在Old Generation块中,垃圾回收一般用mark-compact的算法,速度慢些,但减少内存要求。
  PS:还有个Permanent Generation,主要用来放JVM自己的反射对象,比如类对象和方法对象等。
  垃圾回收描述:
  现在收集器都是采用分代收集算法,堆被划分为新生代和老年代。新生代主要存储新创建的对象和尚未进入老年代的对象。老年代存储经过多次新生代GC(Minor GC)任然存活的对象。
  Minor GC:即新生代GC,指发生在新生代的垃圾收集动作,因为Java对象大多都具备朝生夕灭的特性,所以Minor GC非常频繁,一般回收速度也比较快。垃圾回收分多级是1级或以上为部分垃圾回收,只会回收NEW中的垃圾。
  Major GC &/ Full GC:老年代GC,指发生在老年代的GC,出现了Major GC,经常会伴随至少一次的Minor GC(但非绝对的,在 ParallelScavenge 收集器的收集策略里就有直接进行 Major GC的策略选择过程) 。MajorGC 的速度一般会比Minor GC慢10倍以上。垃圾回收分多级是0级为全部(Full)的垃圾回收,会回收OLD段中的垃圾。
  PS:内存溢出通常发生于OLD段或Perm段垃圾回收后,Eden区仍然无内存空间容纳新的Java对象的情况。
  当一个URL被访问时,内存申请过程如下:
  A. JVM会试图为相关Java对象在Eden中初始化一块内存区域
  B. 当Eden空间足够时,内存申请结束。否则到下一步
  C. JVM试图释放在Eden中所有不活跃的对象(这属于1或更高级的垃圾回收), 释放后若Eden空间仍然不足以放入新对象,则试图将部分Eden中活跃对象放入Survivor区
  D. Survivor区被用来作为Eden及OLD的中间交换区域,当OLD区空间足够时,Survivor区的对象会被移到Old区,否则会被保留在Survivor区
  E. 当OLD区空间不够时,JVM会在OLD区进行完全的垃圾收集(0级)
  F. 完全垃圾收集后,若Survivor及OLD区仍然无法存放从Eden复制过来的部分对象,导致JVM无法在Eden区为新对象创建内存区域,则出现&out of memory错误&
  为什么一些程序频繁发生GC?有如下原因:
  (1)程序内调用了System.gc()或Runtime.gc()。
  &(2)一些中间件软件调用自己的GC方法,此时需要设置参数禁止这些GC。
  (3)Java的Heap太小,一般默认的Heap值都很小。
  (4)频繁实例化对象,Release对象。此时尽量保存并重用对象,例如使用StringBuffer()和String()。
  如果你发现每次GC后,Heap的剩余空间会是总空间的50%,这表示你的Heap处于健康状态。许多Server端的Java程序每次GC后最好能有65%的剩余空间。
  JVM&使用的GC算法是什么?
  分代收集。即将内存分为几个区域,将不同生命周期的对象放在不同区域里;
  在GC收集的时候,频繁收集生命周期短的区域(Young area);
  比较少的收集生命周期比较长的区域(Old area);
  基本不收集的永久区(Perm area)。
  GC何时会被触发?  (1)系统空闲:GC线程的优先级低于系统应用线程,当系统中没有应用线程执行时,GC会被触发。  (2)堆空间内存不足:当堆空间的内存不足以创建新对象时,GC会被触发。如果第一GC仍不能获得足够的空间,第二次GC将被触发,如果这一次仍无法获取足够的空间,&Out of memory&将被抛出。4071人阅读
Java(88)
公司一位大牛在微博上的一条,打算消化一下,毕竟今后Perm区的上涨还是有可能遇到的。“Java应用Perm区一直呈上涨趋势的原因可以用一个简单的办法排查,就是用btrace去跟踪下是什么地方在调用ClassLoader.defineClass,在大多数情况下这招都是管用的。”
(1)Perm区存放的啥信息?
&&&&&&&&Perm叫做持久代,存放了类的信息、类的静态变量、类中final类型的变量、类的方法信息,Perm是全局共享的,在一定条件下会被GC掉,方所要承载的数据超过内存区域后,就会出现OOM异常。可以通过-XX:PermSize和-XX:MaxPermSize来指定这个区域的最小&#20540;和最大&#20540;。持久代的回收主要包括两部分,废弃常量和无用的类,废弃的常量比较容易判别,没有任何String类型的对象引用这个就算可以废弃的了,但是无用的类判别比较复杂,(该类所有的实例已经被回收,JVM中没有任何类的实例;加载该类的ClassLoader被回收;该类对应的java.lang.class没有地方引用)。可以使用-verbose:class以及-XX:TraceClassLoading和-XX:TraceClassUnLoading来查看类的加载和卸载情况。
(2)ClassLoader中的defineClass是干啥的?
&&&&&&&&该方法用于将二进制的字节码转换为Class对象,对于自定义加载类非常重要,如果二进制的字节码不符合JVM的规范,就会报ClassFormatError,如果生成的类名和二进制中的不符,报NoClassDefFoundError异常,如果加载的class是受保护的,则报SecurityException,如果此类已经在ClassLoader已经加载,会报LinkageError。
例子1:运行时常量溢出
向常量池中添加数据,可以调用String.intern(),这个是native方法,如果常量池中已经存在一个,则返回,否则添加早常量池中。
刚开始的例子如下:
public&class&PermTest
&&&&&&&&public&static&void&main(String[]
&&&&&&&&&&&&int&i
&&&&&&&&&&&&while(true){
&&&&&&&&&&&&&&&&String.valueOf(i&#43;&#43;).intern();
&&&&&&&&&&&&&&&&System.out.println(i);
&&&&&&&&&&&&}
发现没有抛出OOM异常,用JConsole查看,(PermSize和MaxPermSize都是10M)在10M的时候曲线又下去了,原来是FullGC把常量池中没有的引用GC掉了,所以没有OOM。
增加了一个List,持有这个引用,就不会被GC掉了。
public&class&PermTest
&&&&&&&&public&static&void&main(String[]
&&&&&&&&&&&&List&String&
all =&new&ArrayList&String&();
&&&&&&&&&&&&int&i
&&&&&&&&&&&&while(true){
&&&&&&&&&&&&&&&&all.add(String.valueOf(i&#43;&#43;).intern());
&&&&&&&&&&&&}
in thread&&main&&java.lang.OutOfMemoryError:
PermGen space
java.lang.String.intern(Native Method)
PermTest.main(PermTest.java:10)
例子2:用cglib产生代理类,不断的创建类
import&java.lang.reflect.M
import&net.sf.cglib.proxy.E
import&net.sf.cglib.proxy.MethodI
import&net.sf.cglib.proxy.MethodP
public&class&PermTest
&&&&public&static&void&main(String[]
&&&&&&&&while(true){
&&&&&&&&&&&&Enhancer
enhancer =&new&Enhancer();
&&&&&&&&&&&&enhancer.setSuperclass(PermTestClass.class);
&&&&&&&&&&&&enhancer.setUseCache(false);
&&&&&&&&&&&&enhancer.setCallback(new&MethodInterceptor()
&&&&&&&&&&&&&&&&@Override
&&&&&&&&&&&&&&&&public&Object
intercept(Object obj, Method method, Object[] args,
&&&&&&&&&&&&&&&&&&&&&&&&MethodProxy
proxy)&throws&Throwable
&&&&&&&&&&&&&&&&&&&&return&proxy.invoke(obj,
&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&});
&&&&&&&&&&&&enhancer.create();
&&&&static&class&PermTestClass{
by: java.lang.OutOfMemoryError: PermGen space
java.lang.ClassLoader.defineClass1(Native Method)
java.lang.ClassLoader.defineClassCond(ClassLoader.java:631)
java.lang.ClassLoader.defineClass(ClassLoader.java:615)
&&&&...&8&more
Btrace的脚本文件如下(查看哪里调用了defineClass信息):
import&static&com.sun.btrace.BTraceUtils.*;
import&com.sun.btrace.annotations.*;
public&class&BtraceAll
&&&&private&static&long&beginT
&&&&@OnMethod(
&&&&&&&&&&&&clazz=&java.lang.ClassLoader&,
&&&&&&&&&&&&method=&defineClass&
&&&&public&static&void&traceMethodBegin(){
&&&&&&&&beginTime
= timeMillis();
&&&&@OnMethod(
&&&&&&&&&&&&clazz=&java.lang.ClassLoader&,
&&&&&&&&&&&&method=&defineClass&,
&&&&&&&&&&&&location=@Location(Kind.RETURN)
&&&&public&static&void&traceMethdReturn(
&&&&&&&&&&&&@Return&String
&&&&&&&&&&&&@ProbeClassName&String
clazzName,
&&&&&&&&&&&&@ProbeMethodName&String
methodName){
&&&&&&&&println(&===========================================================================&);
&&&&&&&&println(strcat(strcat(clazzName,&&.&),
methodName));
&&&&&&&&println(strcat(&Time
taken : &,
str(timeMillis() - beginTime)));
&&&&&&&&println(&java
thread method trace:---------------------------------------------------&);
&&&&&&&&jstack();
&&&&&&&&println(&----------------------------------------------------------------------------&);
&&&&&&&&println(strcat(&Reuslt
:&,str(result)));
&&&&&&&&println(&============================================================================&);
运行后,发现cglib在创建代理类的时候,确实调用了defineClass方法
===========================================================================
java.lang.ClassLoader.defineClass
taken :&79
thread method trace:---------------------------------------------------
java.lang.ClassLoader.defineClass(ClassLoader.java:615)
sun.reflect.GeneratedMethodAccessor1.invoke(Unknown
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
java.lang.reflect.Method.invoke(Method.java:597)
net.sf.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:384)
net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:219)
net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)
net.sf.cglib.proxy.Enhancer.create(Enhancer.java:285)
PermTest.main(PermTest.java:19)
----------------------------------------------------------------------------
:class&PermTest$PermTestClass$$EnhancerByCGLIB$$1ed16944_8
============================================================================
至此,问题场景以及如何排查,清晰了。。。
参考书籍:
1、分布式java应用基础
2、深入理解JVM
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:621976次
积分:7295
积分:7295
排名:第1971名
原创:127篇
转载:336篇
评论:49条
(15)(5)(30)(2)(1)(1)(3)(7)(9)(4)(3)(4)(4)(16)(12)(5)(3)(3)(5)(3)(3)(24)(6)(12)(42)(13)(71)(86)(27)(3)(1)(6)(15)(1)(1)(3)(1)(1)(2)Java基础(8)
虚拟机中的共划分为三个代:年轻代(Young Generation)、年老点(Old Generation)和持久代(Permanent Generation)。其中持久代主要存放的是Java类的类信息,与垃圾收集要收集的Java对象关系不大。年轻代和年老代的划分是对垃圾收集影响比较大的。
  年轻代:
  所有新生成的对象首先都是放在年轻代的。年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。年轻代分三个区。一个Eden区,两个 Survivor区(一般而言)。大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到Survivor区(两个中的一个),当这个 Survivor区满时,此区的存活对象将被复制到另外一个Survivor区,当这个Survivor去也满了的时候,从第一个Survivor区复制过来的并且此时还存活的对象,将被复制“年老区(Tenured)”。需要注意,Survivor的两个区是对称的,没先后关系,所以同一个区中可能同时存在从Eden复制过来对象,和从前一个Survivor复制过来的对象,而复制到年老区的只有从第一个Survivor去过来的对象。而且,Survivor区总有一个是空的。同时,根据程序需要,Survivor区是可以配置为多个的(多于两个),这样可以增加对象在年轻代中的存在时间,减少被放到年老代的可能。
  年老代:
  在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。
  持久代:
  用于存放静态文件,如今Java类、方法等。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如Hibernate 等,在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。持久代大小通过-XX:MaxPermSize=&N&进行设置。
什么情况下触发垃圾回收:
&&& 由于对象进行了分代处理,因此垃圾回收区域、时间也不一样。GC有两种类型:Scavenge GC和Full GC。
&&& Scavenge GC
&&& 一般情况下,当新对象生成,并且在Eden申请空间失败时,就会触发Scavenge GC,对Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。这种方式的GC是对年轻代的Eden区进行,不会影响到年老代。因为大部分对象都是从Eden区开始的,同时Eden区不会分配的很大,所以Eden区的GC会频繁进行。因而,一般在这里需要使用速度快、效率高的算法,使Eden去能尽快空闲出来。
&&& Full GC
&&& 对整个堆进行整理,包括Young、Tenured和Perm。Full GC因为需要对整个对进行回收,所以比Scavenge GC要慢,因此应该尽可能减少Full GC的次数。在对JVM调优的过程中,很大一部分工作就是对于FullGC的调节。有如下原因可能导致Full GC:
年老代(Tenured)被写满持久代(Perm)被写满System.gc()被显示调用上一次GC之后Heap的各域分配策略动态变化
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:1164次
排名:千里之外
转载:13篇
(7)(8)(1)(2)49319人阅读
技术积累(62)
JVM内存组成结构
JVM栈由堆、栈、本地方法栈、方法区等部分组成,结构图如下所示:
所有通过new创建的对象的内存都在堆中分配,其大小可以通过-Xmx和-Xms来控制。堆被划分为新生代和旧生代,新生代又被进一步划分为Eden和Survivor区,最后Survivor由From Space和To Space组成,结构图如下所示:
新生代。新建的对象都是用新生代分配内存,Eden空间不足的时候,会把存活的对象转移到Survivor中,新生代大小可以由-Xmn来控制,也可以用-XX:SurvivorRatio来控制Eden和Survivor的比例
旧生代。用于存放新生代中经过多次垃圾回收仍然存活的对象
每个线程执行每个方法的时候都会在栈中申请一个栈帧,每个栈帧包括局部变量区和操作数栈,用于存放此次方法调用过程中的临时变量、参数和中间结果
3)本地方法栈
用于支持native方法的执行,存储了每个native方法调用的状态
存放了要加载的类信息、静态变量、final类型的常量、属性和方法信息。JVM用持久代(Permanet Generation)来存放方法区,可通过-XX:PermSize和-XX:MaxPermSize来指定最小值和最大值
垃圾回收机制
JVM分别对新生代和旧生代采用不同的垃圾回收机制
新生代的GC:
新生代通常存活时间较短,因此基于Copying算法来进行回收,所谓Copying算法就是扫描出存活的对象,并复制到一块新的完全未使用的空间中,对应于新生代,就是在Eden和From Space或To Space之间copy。新生代采用空闲指针的方式来控制GC触发,指针保持最后一个分配的对象在新生代区间的位置,当有新的对象要分配内存时,用于检查空间是否足够,不够就触发GC。当连续分配对象时,对象会逐渐从eden到survivor,最后到旧生代,
用java visualVM来查看,能明显观察到新生代满了后,会把对象转移到旧生代,然后清空继续装载,当旧生代也满了后,就会报outofmemory的异常,如下图所示:
在执行机制上JVM提供了串行GC(Serial GC)、并行回收GC(Parallel Scavenge)和并行GC(ParNew)
在整个扫描和复制过程采用单线程的方式来进行,适用于单CPU、新生代空间较小及对暂停时间要求不是非常高的应用上,是client级别默认的GC方式,可以通过-XX:+UseSerialGC来强制指定
2)并行回收GC
在整个扫描和复制过程采用多线程的方式来进行,适用于多CPU、对暂停时间要求较短的应用上,是server级别默认采用的GC方式,可用-XX:+UseParallelGC来强制指定,用-XX:ParallelGCThreads=4来指定线程数
与旧生代的并发GC配合使用
旧生代的GC:
旧生代与新生代不同,对象存活的时间比较长,比较稳定,因此采用标记(Mark)算法来进行回收,所谓标记就是扫描出存活的对象,然后再进行回收未被标记的对象,回收后对用空出的空间要么进行合并,要么标记出来便于下次进行分配,总之就是要减少内存碎片带来的效率损耗。在执行机制上JVM提供了串行GC(Serial MSC)、并行GC(parallel MSC)和并发GC(CMS),具体算法细节还有待进一步深入研究。
以上各种GC机制是需要组合使用的,指定方式由下表所示:
新生代GC方式
旧生代GC方式
-XX:+UseSerialGC
-XX:+UseParallelGC
并行回收GC
-XX:+UseConeMarkSweepGC
-XX:+UseParNewGC
-XX:+UseParallelOldGC
并行回收GC
-XX:+ UseConeMarkSweepGC
-XX:+UseParNewGC
不支持的组合
1、-XX:+UseParNewGC -XX:+UseParallelOldGC
2、-XX:+UseParNewGC -XX:+UseSerialGC
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:2286107次
积分:18546
积分:18546
排名:第303名
原创:152篇
转载:14篇
评论:928条
(1)(1)(1)(1)(5)(3)(3)(7)(1)(2)(1)(2)(12)(7)(3)(2)(2)(2)(3)(6)(3)(2)(8)(19)(11)(2)(7)(9)(5)(7)(12)(7)(9)

我要回帖

更多关于 jvm perm区 的文章

 

随机推荐