深入浅出JVM

JAVA平台

这里写图片描述

  1. 先通过一张图直观的认识一下,可以看到JVM对下屏蔽了Platforms的细节,这也是为什么Java能跨平台;
  2. 可以看到JVM有两种,不过1.8以后Client模式基本没有了;
  3. JRE除了包含JVM以后,还包含了底层的类库,很多都是C和C++写的;
  4. JDK除了包含JRE,还包含了像javac这种编译工具和像JConsole的控制工具;

JVM运行时的数据区域

这里写图片描述

  • 程序计数器:指向当前线程正在执行的字节码指令的地址(行号)
  • 虚拟机栈:存储当前线程运行方法时所需要的数据,指令和返回地址
  • 本地方法栈(native):本地方法运行时所需要的数据,指令和返回地址
  • 方法区:类信息、常量
  • 堆(heap):对象

通过图也可以形象的看出,程序计数器、虚拟机栈、本地方法栈都是线程私有的,不存在线程安全问题,而堆和方法区是线程公有的。

虚拟机栈

下面具体聊一聊虚拟机栈(通过我们具体的方法的例子):

public void methodOne(int i) {
    int j = 0;
    int sum = i + j;
    Object acb = obj;
    long start = System.currentTimeMillis();
    methodTwo();
    return;
}

通过javap反编译class文件
这里写图片描述

可以通过下图看出虚拟机栈的作用及如何工作,每个方法都对应一个方法栈帧

这里写图片描述

JVM的内存模型

这里写图片描述

如果所示,内存被分为新生代、老年代、永久代。新生代又被分为8:1:1的eden区、s0和s1区。颜色和上面的JVM的数据区域对应,在小于1.8的时候,永久代在方法区上。那么当大量加载类的时候,很容易出现OutOfMemoryError,在1.8之后,永久代出现了元空间,是可以自动扩容的,好处显而易见,坏处就是可能会无限扩张,侵占别的内存空间。

假设现在理想状态下不触发大对象,阈值(threshold)和 age的状况下,new出一个9兆的对象,首先会放在eden区,满了,会触发Minor gc,这个8兆的对象会被放到s0,但是s0放不下,会有一个担保机制,直接被放进老年代。

GC

Java 中的堆也是 GC 收集垃圾的主要区域。
GC 分为两种:Minor GC、Full GC ( 或称为 Major GC )。

Minor GC 是发生在新生代中的垃圾收集动作,所采用的是复制算法
新生代几乎是所有 Java 对象出生的地方,即 Java 对象申请的内存以及存放都是在这个地方。Java 中的大部分对象通常不需长久存活,具有朝生夕灭的性质。
当一个对象被判定为 “死亡” 的时候,GC 就有责任来回收掉这部分对象的内存空间。新生代是 GC 收集垃圾的频繁区域。
当对象在 Eden ( 包括一个 Survivor 区域,这里假设是 from 区域 ) 出生后,在经过一次 Minor GC 后,如果对象还存活,并且能够被另外一块 Survivor 区域所容纳( 上面已经假设为 from 区域,这里应为 to 区域,即 to 区域有足够的内存空间来存储 Eden 和 from 区域中存活的对象 ),则使用复制算法将这些仍然还存活的对象复制到另外一块 Survivor 区域 ( 即 to 区域 ) 中,然后清理所使用过的 Eden 以及 Survivor 区域 ( 即 from 区域 ),并且将这些对象的年龄设置为1,以后对象在 Survivor 区每熬过一次 Minor GC,就将对象的年龄 + 1,当对象的年龄达到某个值时 ( 默认是 15 岁,可以通过参数 -XX:MaxTenuringThreshold 来设定 ),这些对象就会成为老年代。
但这也不是一定的,对于一些较大的对象 ( 即需要分配一块较大的连续内存空间 ) 则是直接进入到老年代。
Full GC 是发生在老年代的垃圾收集动作,所采用的是标记-清除算法
现实的生活中,老年代的人通常会比新生代的人 “早死”。堆内存中的老年代(Old)不同于这个,老年代里面的对象几乎个个都是在 Survivor 区域中熬过来的,它们是不会那么容易就 “死掉” 了的。因此,Full GC 发生的次数不会有 Minor GC 那么频繁,并且做一次 Full GC 要比进行一次 Minor GC 的时间更长。
另外,标记-清除算法收集垃圾的时候会产生许多的内存碎片 ( 即不连续的内存空间 ),此后需要为较大的对象分配内存空间时,若无法找到足够的连续的内存空间,就会提前触发一次 GC 的收集动作。

OOM异常

导致OutOfMemoryError异常的常见原因有以下几种:

  1. 内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
  2. 集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
  3. 代码中存在死循环或循环产生过多重复的对象实体;
  4. 使用的第三方软件中的BUG;
  5. 启动参数内存值设定的过小;

此错误常见的错误提示:

  1. tomcat:java.lang.OutOfMemoryError: PermGen space
  2. tomcat:java.lang.OutOfMemoryError: Java heap space
  3. weblogic:Root cause of ServletException java.lang.OutOfMemoryError
  4. resin:java.lang.OutOfMemoryError
  5. java:java.lang.OutOfMemoryError

OOM解决办法

对tomcat容器,可以在启动时对jvm设置内存限度。对tomcat,可以在catalina.bat中添加:

set CATALINA_OPTS=-Xms128M -Xmx256M
set JAVA_OPTS=-Xms128M -Xmx256M

其中”-Xms128M”为最小内存,”-Xmx256M”为最大内存。

这个其实用过eclipse的人都知道,当配置的环境变量是jdk1.8以上的时候,eclipse打开都会报错,这个时候,就需要去ini文件设置内存大小。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值