JVM笔记

来源:互联网 发布:不会函数能学编程吗 编辑:程序博客网 时间:2024/05/08 00:05

JVM数据区域图

这里写图片描述

方法区(永久代)

  • 方法区(Method Area)与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等
    数据。虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做Non-Heap(非堆),目的应该是与Java堆区分开来。
  • 对于习惯在HotSpot虚拟机上开发和部署程序的开发者来说,很多人愿意把方法区称为“永久代”(Permanent Generation)对于其他虚拟机(如BEA JRockit、IBM J9等)来说是不存在。 Java虚拟机规范对这个区域的限制非常宽松,这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载。
  • Class文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。
  • PermGen space错误的根本原因:JVM需要加载一个新类的定义,而永久代(PermGen)的空间不足——已经有太多的类存储在那里了。一个可能的原因是:你的应用程序或服务器使用了太多的类,当前的永久代(PermGen)大小无法满足需求。另一个原因可能是内存泄漏。通常泄露的原因是一个长时间运行的线程在应用卸载之后仍旧存在着但是它引用着classloader 而loader又引用着所有类定义等信息 导致每次发布系统时,都会有一个线程导致大量的泄露。详见原文
  • java8已经去掉了永久代 不会再出现PermGen 的OM问题。

Java 堆区

  • Java堆由Old区和New区(也叫Young区)组成,New区由Eden区、From区和To区(Survivor区)组成。
    新生小对象先放到Eden,一次Minor GC后放到From, 之后从From to之间来回移动 每移动一次就长一岁,大于某岁则进入Old区间。

  • Major GC的时间常常是Minor GC的几十倍。JVM内存调优的重点,减少Major GC 的次数,因为为Major GC 会暂停程序比较长的时间,如果Major GC 的次数比较多,意味着应用程序的JVM内存参数需要进行调整。

  • 虚拟机并不总是要求对象的年龄必须达到MaxTenuringThreshold才能晋升老年代,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代。Minor GC后Survivor空间不足就直接放入Old区。大对象直接进入老年代。

JVM 内存回收

基本概念

  • JVM采用一种分代回收的策略,用较高的频率对年轻的对象进行扫描和回收,这种叫做minor collection,而对老对象的检查回收频率要低很多,称为major collection。
  • Java New IO (NIO)为了获得更高的效率,防止jvm的堆内存和系统内存做多一层的映射,使用了 DirectMemory的方式。例如NIO中的MappedByteBuffer,DirectByteBuffer。直接从操作系统分配内存,也成为“堆外内存”。这部分内存不受GC的直接管理,但是效率很高。使用时要比较小心
  • 寻找垃圾对象的方法
    – 1引用计数法(经典 但是sun hotpot未使用 存在循环引用问题)
    – 2 根搜索法(hotspot采用的方法)

引用类型

-JDK1.2后 引用分为了多种
– 强引用:类似Object obj = new Object() 我们直观上认为的引用。
– 软引用: 一些还有用但非必须的对象,在内存溢出前会进行回收,回收后内存仍旧不足才抛出异常
– 弱引用: 无论当前内存是否足够,都会回收掉只被弱引用关联的对象
– 虚引用: 不会对生存时间产生任何影响,其存在的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。

finalize的影响

任何一个对象的finalize只会被系统调用一次!!!!!,在finalize中可以在垃圾回收时救自己一命 也就只能救一次 因为该方法只会被调用一次。
在hotspot的可达性算法即跟搜索法中,不可达的对象要经历两次标记过程,第一次标记过程中,对于覆盖了finalize方法且从未执行过该方法的对象放入F-Queue队列等待执行finalize方法(由低优先级的finalize线程去执行,只是执行不保证会等待执行结束,因为可能在其中发生了死循环或执行慢耽误其它对象执行) 在finalize中重新将对象与引用链上的一个对象关联上就可以拯救自己一次。

方法区(hotspot的永久代)的回收

  • 在大量使用反射、动态代理、CGLIB等框架,动态生成JSP以及OSGI这类频繁自定义ClassLoader的场景都需要虚拟机具备类卸载功能,以保证永久代不会溢出。
  • 方法区回收效率会较低,每次回收不了多少东西,主要回收废弃常量和无用类,另外无用类的回收是否开启Hotspot提供了-Xnoclassgc来配置。
  • 无用类的满足条件:
    –该类的所有实例都已经被回收
    – 加载该类的ClassLoader已经被回收(每个类都会引用自己的ClassLoader ClassLoader也会保持它所加载的每个类的引用!!!!!)
    – 该类对应的Class对象没有在任何地方被引用,无法在任何地方通过反射引用该类的方法。

垃圾收集算法

标记-清除算法

  • 该算法效率低 且会产生大量内存碎片。

复制收集算法

  • 将内存分成两块,等一块用完了,将存活的对象移动到另一块。在实际引用中用于新生代,鉴于一次GC会有%98的新生对象死亡,实际分成了Eden、Survivor两个区间 按照8比2的大小。Survivor又分成from to ,每次会浪费%10的空间不能存放对象,一次minorGC后,存活对象放到这空置的%10上 若超出则放入老生代。

标记-整理算法

  • 老生代的对象存活时间长,浪费不起空间,采用标记-整理算法,即先标记存活对象,然后将存活对象向一端移动。

分代收集算法

-将堆老年代 新生代 ,根据代特点采用以上几种算法实现。

优化

  • 将生成的无用对象,中间对象置为null,加快内存回收。
  • 对象池技术 如果生成的对象是可重用的对象,可以考虑采用对象池来较少对象的生成。
0 0
原创粉丝点击