java程序需要一种方式把其中的对象在内存需求方面的特征传达给垃圾回收器,垃圾回收器根据对象特征可以更好的回收,这就是几种引用的存在意义(强引用、弱引用等等)
java应用的内存泄漏,主要分为两类
- 虚拟机中存在程序无法使用的内存区域。这些内存区域被程序中一些无法使用的存活对象使用,用于存在隐式强引用,无法被回收。但是这些对象也无法被使用
- 程序中存在大量存活时间过长的对象,这些对象的生存时间长于使用他们的对象,这些对象占用内存资源,时间长了,就会报OOM
弱应用
- weakHashMap就是 key是引用
对象的几种状态
- 可达状态,当有引用指向对象。该对象处于可达的,根据引用不同,有可能处于强引用可达,软引用或弱应用可达
- 可复活状态,finalize()方法执行可能让对象复活
- 不可达状态,释放内存
虚引用在执行finalize()之后才会加入队列,由于get方法始终返回null,不存在对象能复活的情况
软引用和弱引用进入队列的时机和finalize方法执行之间 ,没有必然联系
虚引用在执行finalize()之后才会加入队列
java 程序的分析工具
命令行和图形化工具
- jps/jmap/jhat
- jconsole 和 java VisualVM
JMX
JMX API 是java 平台上进行资源监控的API,可以用来对应用程序、设备、服务和Java 虚拟机本身进行监控和管理。JMX有很多作用,包括运行时动态获取和更新程序的配置信息、收集程序运行过程中的统计信息以及当程序内部发生变化或出现错误时的相关通知
JMX API的基础概念是一个Mbean,代表可以被管理的命令资源。MBean是一个接口,需要由被管理的资源提供相关实现,所有的MBean都注册到MBean服务器上。java虚拟机本身提供一个MBean服务器,相关Mbean接口都在java.lang.management 包中。
- 主要由两种使用方式,一种是直接使用API,利用轮询等机制,查询资源使用状态
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryPoolMXBean;
import java.util.List;
/**
* @program: 检测老年代使用是否超过阈值,否则停止任务
* @author: whp
* @create: 2020-02-27 23:05
**/
public class BackupTaskRunnable implements Runnable {
private MemoryPoolMXBean poolMXBean;
public BackupTaskRunnable() {
init();
}
private void init(){
List<MemoryPoolMXBean> beans = ManagementFactory.getMemoryPoolMXBeans();
for (MemoryPoolMXBean bean:beans){
//老年代
if("Tenured Gen".equals(bean.getName())){
poolMXBean = bean;
break;
}
}
poolMXBean.setUsageThreshold(10*1024*1024);
}
@Override
public void run() {
while (true){
//循环检测年老代的使用情况
if(poolMXBean.isUsageThresholdExceeded()){
System.out.println("memory exceed,mission abort");
}else{
System.out.println("mission continue");
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
- 另一种是使用Mbean的事件监听机制,某些Mbean在内部发生状态变化的时候,会产生相应的事件通知。Mbean的使用者可以在事件通知上注册监听器
public class MemoryListener implements NotificationListener {
@Override
public void handleNotification(Notification notification, Object handback) {
String type = notification.getType();
if(type.equals(MemoryNotificationInfo.MEMORY_THRESHOLD_EXCEEDED)){
System.out.println("memory exceed,mission abort");
}
}
}
//增加监听器
public void addListener(){
MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
NotificationEmitter emitter = (NotificationEmitter) memoryMXBean;
MemoryListener listener = new MemoryListener();
emitter.addNotificationListener(listener,null,null);
}
Java虚拟机工具接口
JMX的 API也是很有限。虚拟机提供的所有功能虽然不能通过Java Api来直接使用,但是可以通过C++原生代码使用,Java对虚拟机中的管理功能进行整合,形成了标准的Java虚拟机工具接口(JVM TI)
使用JVM TI开发的工具被称为虚拟机的代理程序。在虚拟机启动时,代理程序也会被加载和运行。
当JAVA程序运行时,代理程序可以通过JVM TI(接口)查看和控制Java 程序的相关状态。
代理程序本身是底层操作系统的一个原生代码库(C++编译而成)
虚拟机启动的时候通过“-agentlib” 或 “agentPath”指定原生代码库地址或绝对路径