一:直接上干货,一下参数可以直接用:jmap -heap 进程号来查看

        -Xss:栈大小

       -Xms:堆的最小值;

       -Xmx:堆的最大值;

        -Xmn:新生代的大小;

        -XX:NewRatio 老年代(不包括永久区)与新生代(Eden + from+to)的比值;

               要求比较的搞得吞吐量(避免较多的fullGC)4 表示新生代 :老年代 = 1:4 ,意思是老年代占 4/5 

       -XX:SurvivorRatio:8表示Eden区与Survivor区的大小比值是8:1:1,因为Survivor区有两个(from, to)

           -XX:NewSize;新生代最小值;

         -XX:MaxNewSize:新生代最大值

         -XX:PermSize;-XX:MaxPermSize;方法区/永久代(dk1.7及以前)

         -XX:MetaspaceSize; -XX:MaxMetaspaceSize 方法区/永久代 jdk1.8以后,大小就只受本机总内存的限制

         -XX:MaxDirectMemorySize ;直接内存

类型

参数

运行模式

-sever

整个堆内存大小

为-Xms和-Xmx设置相同的值。

新生代空间大小

-XX:NewRatio: 2到4. -XX:NewSize=? –XX:MaxNewSize=?. 使用NewSize代替NewRatio也是可以的。

持久代空间大小

-XX:PermSize=256m -XX:MaxPermSize=256m. 设置一个在运行中不会出现问题的值即可,这个参数不影响性能。

GC日志

-Xloggc:$CATALINA_BASE/logs/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps. 记录GC日志并不会特别地影响Java程序性能,推荐你尽可能记录日志。

GC算法

-XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75. 一般来说推荐使用这些配置,但是根据程序不同的特性,其他的也有可能更好。

发生OOM时创建堆内存转储文件

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$CATALINA_BASE/logs

发生OOM后的操作

-XX:OnOutOfMemoryError=$CATALINA_HOME/bin/stop.sh 或 -XX:OnOutOfMemoryError=$CATALINA_HOME/bin/restart.sh. 记录内存转储文件后,为了管理的需要执行一个合适的操作。

 

二:运行时数据区域

         1:运行时数据区域:当前线程执行的字节码的行号指示器,占用空间小,也无法干涉

          2:虚拟机栈:

                 每个线程私有的,线程在运行时,在执行每个方法的时候都会打包成一个栈帧,存储了局部变量表,操作数栈,动态链接,方法出口等信息,然后放入栈。每个时刻正在执行的当前方法就是虚拟机栈顶的栈桢。方法的执行就对应着栈帧在虚拟机栈中入栈和出栈的过程。栈桢大小缺省为1M,可用参数 –Xss调整大小,例如-Xss256k

            3:堆:几乎所有对象都分配在这里,也是垃圾回收发生的主要区域

             4:方法区/永久代:用于存储已经被虚拟机加载的类信息,常量("zdy","123"等),静态变量(static变量)等数据,可用以下参数调整

             5:直接内存:

不是虚拟机运行时数据区的一部分,也不是java虚拟机规范中定义的内存区域;如果使用了NIO,这块区域会被频繁使用,在java堆内可以用directByteBuffer对象直接引用并操作;

这块内存不受java堆大小限制,但受本机总内存的限制,可以通过-XX:MaxDirectMemorySize来设置(默认与堆内存最大值一样),所以也会出现OOM异常

             6:栈上分配

虚拟机提供的一种优化技术,基本思想是,对于线程私有的对象,将它打散分配在栈上,而不分配在堆上。好处是对象跟着方法调用自行销毁,不需要进行垃圾回收,可以提高性能。

栈上分配需要的技术基础,逃逸分析。逃逸分析的目的是判断对象的作用域是否会逃逸出方法体。注意,任何可以在多个线程之间共享的对象,一定都属于逃逸对象。

public void test(int x,inty ){

String x = “”;

User u = ….

…..

}

User类型的对象u就没有逃逸出方法test。

public  User test(int x,inty ){

String x = “”;

User u = ….

…..

return u;

}

User类型的对象u就逃逸出方法test。

如何启用栈上分配

-server JVM运行的模式之一, server模式才能进行逃逸分析, JVM运行的模式还有mix/client

-Xmx10m和-Xms10m:堆的大小

-XX:+DoEscapeAnalysis:启用逃逸分析(默认打开)

-XX:+PrintGC:打印GC日志

-XX:+EliminateAllocations:标量替换(默认打开)

-XX:-UseTLAB 关闭本地线程分配缓冲

TLAB: ThreadLocalAllocBuffer,具体解释参见下文《虚拟机中的对象---对象的分配----2)》

对栈上分配发生影响的参数就是三个,-server、-XX:+DoEscapeAnalysis和-XX:+EliminateAllocations,任何一个发生变化都不会发生栈上分配,因为启用逃逸分析和标量替换默认是打开的,所以,在我们的例子中,JVM的参数只用-server一样可以有栈上替换的效果(以Mark老师机器上JDK1.8为例,其他版本JDK请自行尝试)

对象的内存布局

在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。

对象头包括两部分信息,第一部分用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。

对象头的另外一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。

第三部分对齐填充并不是必然存在的,也没有特别的含义,它仅仅起着占位符的作用。由于HotSpot VM的自动内存管理系统要求对对象的大小必须是8字节的整数倍。当对象其他数据部分没有对齐时,就需要通过对齐填充来补全

三:OOM案例分析

出现java.lang.OutOfMemoryError: GC overhead limit exceeded  一般是(某个循环里可能性最大)在不停的分配对象,但是分配的太多,把堆撑爆了。

出现java.lang.OutOfMemoryError: Java heap space一般是分配了巨型对象

栈溢出

参数:-Xss256k

java.lang.StackOverflowError  一般的方法调用是很难出现的,如果出现了要考虑是否有无限递归。

虚拟机栈带给我们的启示:方法的执行因为要打包成栈桢,所以天生要比实现同样功能的循环慢,所以树的遍历算法中:递归和非递归(循环来实现)都有存在的意义。递归代码简洁,非递归代码复杂但是速度较快。