码迷,mamicode.com
首页 > 其他好文 > 详细

JVM学习笔记

时间:2015-05-03 23:29:49      阅读:159      评论:0      收藏:0      [点我收藏+]

标签:

一、多线程JVM实例

一个JVM实例可以包含很多个线程。一个实体机器包含多个JVM实例,绑定不同的端口。
线程共享的区域:方法区,常量缓冲池,堆。
每个线程私有:程序计数器,栈,本地方法栈。
 
二、JVM运行机制
JVM执行程序:
1)加载.class文件。
2)管理并分配内存
3)进行GC。
JVM的两种机制:
1)装载合适的类或接口,叫做类状态子系统。
2)执行已经装载的类或接口,叫做运行引擎。
 
 三、运行时数据区域
1、JVM实例中每个线程私有
程序计数器:制定下一条执行的字节码,包含分支,循环,跳转,异常处理,线程恢复等基础功能。
虚拟机栈:用来存储局部变量表(各种数据类型:int,float),操作数栈,动态链接,方法出口等信息。
本地方法栈:和上面的区别在于虚拟机栈执行JAVA方法(字节码)服务,本地方法栈执行Native方法服务。 
2、JVM所有线程公用
JAVA堆:只存放各种对象实例,gc的主要区域,分为Eden,supervivor*2,old,permanent区域。
方法区:存放虚拟机加载的类信息,常量,静态变量,编译后的代码等数据。
常量池:类被加载到方法区中后,用来存放常量。
3、直接内存
JDK1.4加入了NIO类,基于通道和缓冲区的IO方式,使用Native函数直接使用堆外内存。
 
四、对象和OOM问题
1、对象的创建
     new指令后,会去常量池中查看这个类是否被加载,解析,初始化。然后就在Java堆中分配内存存放对象,将对象init后放入堆中。所有的关于对象的信息都放入到对象头信息中。
     Thread Local Allocation Buffer (TLAB) 本地线程分配缓冲,每个线程都会有一个缓冲,分配对象使用。
2、对象的结构
     对象头(header):一是存放对象自身运行数据(哈希码,GC分代年龄,锁状态标识,线程持有锁,偏向线程ID,偏向时间戳);二是存放类元数据指针,通过这个指针判断是哪个类的对象。
     实例数据(Instance Data):存放真正的数据信息。
     对齐填充(Padding):只是占位符,可忽略。
3、访问对象
     Java程序(线程)是通过栈(线程私有)上的reference数据来操作对象的具体对象。
     两种访问对象的方法:
     1)使用句柄。Java在堆中添加一个句柄池,reference中存放的是对象的句柄地址。优点:对象被移动,只改变句柄池中指针,reference本身不改。
     2)直接指针。在实例数据区直接存放指向对象的指针。优点:节省指针定位的时间开销。 
4、常见的OOM问题
     OutOfMemoryError  内存溢出问题。
     造成这种问题的原因有:
     1)堆溢出。Java Heap space。大量的对象或者大对象占满堆导致。
          排查问题:可以通过内存映像分析工具,将dump下来的内存快照进行分析。
          问题原因:有两个原因,一种是真的堆内存太小(Memory overflow),不满足生产需要。二是内存泄露了(Memory leak)。如果是第一种,可以先调整堆的大小试试看(-Xmx和-Xms),如果还是小,就该升级硬件,或者取消机器多实例。如果是Memory Leak,那就是develop的问题了。    
     2)本地方法栈和虚拟机栈溢出。
          这个会出现两种错误,一种是StackOverflowError,一种是OutOfMemoryError。如果你申请的栈深度大于JVM规定的栈深度,返回第一种。如果JVM在扩展栈时无法获取内存,则返回第二种。一般单线程申请栈,都是第一种。创建多线程的时候使用栈,则是第二种问题。 
          问题原因:一是 -Xss参数调整栈内存容量过小;二是定义了大量本地变量;三是创建过多的线程,分配栈的时候内存溢出。可以使用jstack查看。
     3) 方法区和常量池溢出。
          在JDK1.6之前的版本(含1.6),常量池分配在永久代中,可以通过参数 -XX:PermSize 和 -XX:MaxPermSize 限制大小。
          问题原因:一是代码中,大量产生动态类(类名)或者常量放入这个空间。二是此区域配置的太小了。
     4)本机直接内存溢出。
          可通过参数 -XX:MaxDirectMemorySize 配置。使用直接内存的类DirectByteBuffer类和Unsafe类分配内存过多。
          问题原因:和上面思路一样。
     
 五、垃圾收集器GC和内存分配 
1、判断对象死亡
     1)引用计数算法:每个对象有引用计数器,当有地方引用他就加1,引用失效就减1,为0代表对象死亡。但是互相循环引用这样就无法判断。
     2)可达性分析算法:GC Roots为起始点,GC Roots到对象不可达(没有引用链--图论),则判定该对象缓刑(两次标记代表死亡),当虚拟机执行过了finalize()或者对象没有覆盖这个方法,则目前该对象活着。这个方法是JVM调用,将对象推向火坑的方法。Jvm认为该对象有必要推向火坑,就将该对象放到F-QUEUE队列中。第二次就实行垃圾回收。     
     补充说明:引用。
     1)强引用:new。这种引用的对象不会回收。
     2)软引用:有用非必要的对象。内存要溢出时回收。具体我不是开发,也不清楚这是什么对象。
     3)弱引用:非必要对象,生存到下次GC。WeakReference类实现弱引用
     4)虚引用:无法通过虚引用获得对象实例。设置这个的目的只是当它被回收的时候,获得一个系统通知。PhantomReference类实现。 
2、内存分配和回收策略(重点)
     eden  supervivor1  supervivor2    old   permanent
                    young       8:1       old
     策略:    
     1)minorGC:新对象放到Eden区中。当eden满了,开始yountGC或者叫minorGC。清除非存活对象,并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。
      2)一般情况下,当新对象生成,并且在Eden申请空间失败时,就触发了Scavenge GC。
     3)Full GC :对整个堆进行整理,包括Young、Tenured和Perm。Full GC比Scavenge GC要慢,因此应该尽可能减少Full GC。有如下原因可能导致Full GC:Tenured被写满;Perm域被写满;System.gc()被显示调用;上一次GC之后Heap的各域分配策略动态变化
     4)大对象直接进入old代,避免在supervivor中来回复制耗时间。配置参数 -XX:PretenureSizeThreshold。
     5)长期存活的对象进old代。配置参数 -XX:MaxTenuringThreshold=15,表示15次youngGC后进入old代,之前则一直在两个supervivor区来回复制。高级的方法可以动态年龄判定:高于平均年龄的对象,进入old代。 
3、垃圾回收算法
     1)标记-清除
     2)复制
     3)标记-整理
     4)分代收集
     hotspot应用算法
     1)枚举根节点
     2)安全点
     3)安全区域
4、垃圾回收器
Serial,ParNew,Parallel Scavenge,Serial Old,Parallel Old,CMS,G1
 
六、JVM查看性能命令

jps

  • 查看所有的jvm进程,包括进程ID,进程启动的路径等等。
  • 我自己也用PS,即:ps -ef | grep java

jstack

  • 观察jvm中当前所有线程的运行情况和线程当前状态。
  • 系统崩溃了?如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题。
  • 系统hung住了?jstack工具还可以附属到正在运行的java程序中,看到当时运行的java程序的java stack和native stack的信息, 如果现在运行的java程序呈现hung的状态,jstack是非常有用的。

jstat

  • jstat利用JVM内建的指令对Java应用程序的资源和性能进行实时的命令行的监控,包括了对进程的classloader,compiler,gc情况;
  • 特别的,一个极强的监视内存的工具,可以用来监视VM内存内的各种堆和非堆的大小及其内存使用量,以及加载类的数量。

jmap

  • 监视进程运行中的jvm物理内存的占用情况,该进程内存内,所有对象的情况,例如产生了哪些对象,对象数量;
  • 系统崩溃了?jmap 可以从core文件或进程中获得内存的具体匹配情况,包括Heap size, Perm size等等

jinfo

  • 观察进程运行环境参数,包括Java System属性和JVM命令行参数
  • 系统崩溃了?jinfo可以从core文件里面知道崩溃的Java应用程序的配置信息。

备注

  • 如果能熟练运用这些命令,尤其是在linux下,那么完全可以代替jprofile等监控工具。
  • 用命令的好处就是速度快,并且辅助于其他命令,比如grep gawk sed等,可以组装多种符合自己需求的工具。
 
七、常见问题
能否导致的常见问题通常有以下几种:
1、youngGC频繁
     一是服务压力大,线程数多,定义的新对象多,很快就占满Eden。二是Eden太小。 
2、youngGC时间长
     一是对象的数据结构过大;二是申请大对象;三是定义有用对象过多,放到Eden区后,在两个supervivor区域中来回复制耗内存导致。 
3、fullGC频繁
4、fullGC时间长。
     往往是分配的堆过大导致。如果你的机器很好(64G内存以上?),建议可以多32位JVM实例部署或者64位JVM处理大内存(后者效果不好),过大的堆,在进行fullGC的时候,占用时间长,服务体验不好。
5、内存溢出或内存泄露

JVM学习笔记

标签:

原文地址:http://www.cnblogs.com/luobaolong/p/4474814.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!