Java 内存管理研究

目录:

JVM 内存结构
垃圾回收机制
内存检测工具

Java 内存管理研究_第1张图片

1.Java虚拟机

Java 内存管理研究_第2张图片

栈区:
1.
每个线程包含一个栈区,栈中只保存原始类型数据和对象和对象引用(不是对象),对象都存放在堆区中
2.每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。
3.栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)
方法区
:
1.
又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的classstatic变量。
2.方法区中包含的都是在整个程序中永远唯一的元素,如classstatic变量。

每一个Java线程都有一个程序计数器来用于保存程序执行到当前方法的哪一个指令


Java堆内存


Java 内存管理研究_第3张图片

所有通过new创建的对象的内存都在堆中分配,其大小可以通过-Xmx-Xms来控制

Heap区分两大块,一块是NEWGeneration,另一块是OldGeneration.

NewGeneration中,有一个叫Eden的空间,主要是用来存放新生的对象,还有两个SurvivorSpaces(from,to),它们用来存放每次垃圾回收后存活下来的对象。

OldGeneration中,主要存放应用程序中生命周期长的JVM内存对象。

PermanentGeneration,又称非堆,主要用来放JVM自己的 反射对象,比如类对象和方法对象等


2.垃圾回收机制

 目的:于清除不再使用的对象

触发条件:

             • 当应用程序空闲时 , 即没有应用线程在运行时 ,GC 会被调用。
             • Java 堆内存不足时 ,GC 会被调用。
             • System.gc ()  发送回收建议
             • finalize()  终止对象,释放资源

如何判断对象是否存活?
根搜索算法

  确定一系列的GC Root的对象作为起点,从这些节点向下搜索,搜索的路径称为引用链(Reference Chain),当一个对象无法通过引用链连向GC Root的话证明此对象是不可用的

GC Root 的确定

  1 虚拟机栈中引用的对象

  2 方法区中的类静态属性引用的对象

  3 方法区中常量引用的属性

  4 本地方法栈中Native方法的引用的对象

如何回收不存活的对象?
如果一个对象在搜索后发现没有与 GC Roots 相连接的引用链,就会被第一次标记并且进行一次筛选,看此对象是否有必要执行 finalize() 方法
如果 finalize 方法没有被覆盖或者已经被虚拟机调用过了则将此对象删掉否则此对象将被放入一个名为 F-Queue 的队列之中
虚拟机会自动建立一条低优先级的 Finalizer 线程去执行这个对象的 finalize 方法
如果此次执行该对象还没有与 GC Roots 建立引用链则会在下次 gc 的时候被删除
如何减少GC开销?
不要显式调用 System.gc ()
尽量减少临时对象的使用
对象不用时最好显式置为 null
尽量使用 StringBuffer , 而不用 String 来累加字符串
能用基本类型如 int,long , 就不用 Integer,Long 对象
尽量少用静态对象变量
分散对象创建或删除的时间
内存泄露/内存溢出
内存泄漏

  大量无用且可达的对象,不能被GC回收

内存溢出

  java.lang.OutOfMemoryError:PermGenspace

  JVM中如果98%的时间是用于GC且可用的Heap size 不足2%的时候将抛出此异常信息。

  JVM初始分配的内存由-Xms指定,默认是物理内存的1/64; 

  JVM最大分配的内存由-Xmx指定,默认是物理内存的1/4


3.内存泄露检测工具
可视化工具(不推荐)

  JconsoleVisual VMJProfiler

命令(专业人士,推荐用命令行)

  jpsjmapjstatjstackjinfo


3.1  jmap

输出 jvm 物理内存的占用情况。
参数如下:

  -heap:打印jvm heap的情况

  -histo:打印jvm heap的直方图。其输出信息包括类名,对象数量,对象占用大小

  -histolive:同上,但是只答应存活对象的情况

  -permstat:打印permanent generation heap情况


Java 内存管理研究_第4张图片

其中:

#instance是对象的实例个数

#bytes是总占用的字节数

classname对应的就是Class文件里的class的标识

B代表byte

C代表char

D代表double

F代表float

I代表int

J代表long

Z代表boolean

前边有[代表数组,[I就相当于int[]

对象用[L+类名表示


3.2 Jstack

Java 内存管理研究_第5张图片

1NEW,线程对象被new出来,但是还没有调用start方法,这时就被称为NEW

2RUNNABLE,线程对象已经被调用了start方法,但是,这个线程的run方法可能运行也可能没有运行,依赖于操作系统的行为;

3BLOCKED,简单的说,线程进入了synchronized关键字标识的同步块,但和4状态有所区别;

4WAITINGTIMED_WAITING差不多,一般是调用了对象的wait方法,需要其它线程在特定场景使用notify/notifyAll方法;

5TERMINATEDrun方法退出运行,即进入这个状态;


3.3 Jstat


Java 应用程序的资源和性能进行实时的命令行的监控,包括了对 Heap size 和垃圾回收状况的监控等等
-class :统计 class loader 行为信息
-compile :统计编译行为信息
- gc :统计 jdk gc heap 信息
- gccapacity :统计不同的 generations 相应的 heap 容量情况
- gccause :统计 gc 的情况,(同 - gcutil )和引起 gc 的事件
- gcnew :统计 gc 时,新生代的情况
- gcnewcapacity :统计 gc 时,新生代 heap 容量
- gcold :统计 gc 时,老年区的情况
- gcoldcapacity :统计 gc 时,老年区 heap 容量
- gcpermcapacity :统计 gc 时, permanent heap 容量
- gcutil :统计 gc 时, heap 情况
- printcompilation

Java 内存管理研究_第6张图片

S0Heap上的 Survivorspace 0 段已使用空间的百分比

S1Heap上的 Survivorspace 1 段已使用空间的百分比

EHeap上的 Edenspace 段已使用空间的百分比

OHeap上的 Oldspace 段已使用空间的百分比

PPermspace 已使用空间的百分比

YGC:从程序启动到采样时发生Young GC的次数

YGCTYoungGC所用的时间(单位秒)

FGC:从程序启动到采样时发生Full GC的次数

FGCTFullGC所用的时间(单位秒)

GCT:用于垃圾回收的总时间(单位秒)


如何避免内存泄露/溢出


尽早释放无用对象的引用
字符串处理时,尽量避免使用 String ,而应使用 StringBuffer
尽量少用静态变量
避免集中创建对象尤其是大对象,如果可以的话尽量使用流操作
尽量运用对象池技术以提高系统性能
不要在经常调用的方法中创建对象,尤其是忌讳在循环中创建对象
优化配置


内存泄漏解决方案

从代码层面进行优化完善,尽量避免该情况发生
调整优化服务器配置 

  1) 设置-Xms-Xmx相等

  2)设置NewSizeMaxNewSize相等

  3)设置Heap size, PermGenspace


Tomcat的配置示例:修改 %TOMCAT_HOME%/bin/catalina.bat orcatalina.sh

 在“echo"Using CATALINA_BASE: $CATALINA_BASE"”上面加入以下行:    

Cmd代码:  set JAVA_OPTS=-Xms800m -Xmx800m -XX:PermSize=128M -XX:MaxNewSize=256m -XX:MaxPermSize=256m


Java 内存管理研究_第7张图片


你可能感兴趣的:(Java 内存管理研究)