转(http://club.alibabatech.org/article_detail.htm?articleId=4)

JVM 内部运行线程介绍

作者:蒋家佳/觉梦(支付宝开发工程师) 浏览量: 9448 评论数: 1
hi,all:

最近抽时间把JVM运行过程中产生的一些线程进行了整理,主要是围绕着我们系统jstack生成的文件为参照依据。 前段时间因为系统代码问题,造成性 能到了天花板,于是就dump了一份stack出来进行分析。 看stack其实也需要一定的经验,毕竟它里面很多线程不可能都是有问题,所以,需要对他 们有一定认识。 现在市面上很少有人对这一块做整理,所以,导致很多新人在拿到一个stack文件之后,也是一头雾水。下面我把这次整理的一些个人认为比 较重要的线程列出来,供大家参考。如果发现有什么写得不对或者可以补充的地方,也请朋友们抱着乐于分享的态度灌灌水。

线程名称

所属

解释说明

Attach Listener

JVM

Attach Listener线程是负责接收到外部的命令,而对该命令进行执行的并且吧结果返回给发送者。通常我们会用一些命令去要求jvm给我们一些反馈信息, 如:java -version、jmap、jstack等等。如果该线程在jvm启动的时候没有初始化,那么,则会在用户第一次执行jvm命令时,得到启动。

Signal Dispatcher

JVM

前面我们提到第一个Attach Listener线程的职责是接收外部jvm命令,当命令接收成功后,会交给signal dispather线程去进行分发到各个不同的模块处理命令,并且返回处理结果。signal dispather线程也是在第一次接收外部jvm命令时,进行初始化工作。

CompilerThread0

JVM

用来调用JITing,实时编译装卸class。通常,jvm会启动多个线程来处理这部分工作,线程名称后面的数字也会累加,例如:CompilerThread1

Concurrent Mark-Sweep GC Thread

JVM

并发标记清除垃圾回收器(就是通常所说的CMS GC)线程,该线程主要针对于老年代垃圾回收。ps:启用该垃圾回收器,需要在jvm启动参数中加上:-XX:+UseConcMarkSweepGC

DestroyJavaVM

JVM

执行main()的线程在main执行完后调用JNI中的jni_DestroyJavaVM()方法唤起DestroyJavaVM线程。   JVM在Jboss服务器启动之后,就会唤起DestroyJavaVM线程,处于等待状态,等待其它线程(java线程和native线程)退出时通知 它卸载JVM。线程退出时,都会判断自己当前是否是整个JVM中最后一个非deamon线程,如果是,则通知DestroyJavaVM线程卸载JVM。

ps:

扩展一下:

1.如果线程退出时判断自己不为最后一个非deamon线程,那么调用thread->exit(false),并在其中抛出thread_end事件,jvm不退出。

2.如果线程退出时判断自己为最后一个非deamon线程,那么调用before_exit()方法,抛出两个事件: 事件1:thread_end线程结束事件、事件2:VM的death事件。

然后调用thread->exit(true)方法,接下来把线程从active list卸下,删除线程等等一系列工作执行完成后,则通知正在等待的DestroyJavaVM线程执行卸载JVM操作。

ContainerBackgroundProcessor线程

JBOSS

它是一个守护线程,在jboss服务器在启动的时候就初始化了,主要工作是定期去检查有没有Session过期.过期则清除.

参考:http://liudeh-009.iteye.com/blog/1584876

Dispatcher-Thread-3 线程

Log4j

Log4j具有异步打印日志的功能,需要异步打印日志的Appender都需要注册到AsyncAppender对象里面去,由 AsyncAppender进行监听,决定何时触发日志打印操作。AsyncAppender如果监听到它管辖范围内的Appender有打印日志的操 作,则给这个Appender生成一个相应的event,并将该event保存在一个buffuer区域内。  Dispatcher-Thread-3线程负责判断这个event缓存区是否已经满了,如果已经满了,则将缓存区内的所有event分发到 Appender容器里面去,那些注册上来的Appender收到自己的event后,则开始处理自己的日志打印工作。Dispatcher- Thread-3线程是一个守护线程。

Finalizer线程

JVM

这个线程也是在main线程之后创建的,其优先级为10,主要用于在垃圾收集前,调用对象的finalize()方法;关于Finalizer线程的几点:

1)只有当开始一轮垃圾收集时,才会开始调用finalize()方法;因此并不是所有对象的finalize()方法都会被执行;

2)该线程也是daemon线程,因此如果虚拟机中没有其他非daemon线程,不管该线程有没有执行完finalize()方法,JVM也会退出;

3) JVM在垃圾收集时会将失去引用的对象包装成Finalizer对象(Reference的实现),并放入ReferenceQueue,由Finalizer线程来处理;最后将该Finalizer对象的引用置为null,由垃圾收集器来回收;

4) JVM为什么要单独用一个线程来执行finalize()方法呢?如果JVM的垃圾收集线程自己来做,很有可能由于在finalize()方法中误操作导致GC线程停止或不可控,这对GC线程来说是一种灾难;

Gang worker#0

JVM

JVM用于做新生代垃圾回收(monir gc)的一个线程。#号后面是线程编号,例如:Gang worker#1

GC Daemon

JVM

GC Daemon线程是JVM为RMI提供远程分布式GC使用的,GC Daemon线程里面会主动调用System.gc()方法,对服务器进行Full GC。其初衷是当RMI服务器返回一个对象到其客户机(远程方法的调用方)时,其跟踪远程对象在客户机中的使用。当再没有更多的对客户机上远程对象的引用 时,或者如果引用的“租借”过期并且没有更新,服务器将垃圾回收远程对象。

不过,我们现在jvm启动参数都加上了-XX:+DisableExplicitGC配置,所以,这个线程只有打酱油的份了。

IdleRemover

JBOSS

Jboss连接池有一个最小值,该线程每过一段时间都会被Jboss唤起,用于检查和销毁连接池中空闲和无效的连接,直到剩余的连接数小于等于它的最小值。

Java2D Disposer

JVM

这个线程主要服务于awt的各个组件。说起该线程的主要工作职责前,需要先介绍一下Disposer类是干嘛的。Disposer提供 一个addRecord方法。如果你想在一个对象被销毁前再做一些善后工作,那么,你可以调用Disposer#addRecord方法,将这个对象和一 个自定义的DisposerRecord接口实现类,一起传入进去,进行注册。

Disposer类会唤起“Java2D Disposer”线程,该线程会扫描已注册的这些对象是否要被回收了,如果是,则调用该对象对应的DisposerRecord实现类里面的dispose方法。

Disposer实际上不限于在awt应用场景,只是awt里面的很多组件需要访问很多操作系统资源,所以,这些组件在被回收时,需要先释放这些资源。

InsttoolCacheScheduler_QuartzSchedulerThread

Quartz

InsttoolCacheScheduler_QuartzSchedulerThread是Quartz的主线程,它主要负责实时的获取下一个时间点要触发的触发器,然后执行触发器相关联的作业。

原理大致如下:

Spring和Quartz结合使用的场景下,Spring IOC容器初始化时会创建并初始化Quartz线程池(TreadPool),并启动它。刚启动时线程池中每个线程都处于等待状态,等待外界给他分配 Runnable(持有作业对象的线程)。

继而接着初始化并启动Quartz的主线程 (InsttoolCacheScheduler_QuartzSchedulerThread),该线程自启动后就会处于等待状态。等待外界给出工作信 号之后,该主线程的run方法才实质上开始工作。run中会获取JobStore中下一次要触发的作业,拿到之后会一直等待到该作业的真正触发时间,然后 将该作业包装成一个JobRunShell对象(该对象实现了Runnable接口,其实看是上面TreadPool中等待外界分配给他的 Runnable),然后将刚创建的JobRunShell交给线程池,由线程池负责执行作业。

线程池收到Runnable后,从线程池一个线程启动Runnable,反射调用JobRunShell中的run方法,run方法执行完成之后,TreadPool将该线程回收至空闲线程中。

InsttoolCacheScheduler_Worker-2

Quartz

InsttoolCacheScheduler_Worker-2线程就是ThreadPool线程的一个简单实现,它主要负责分配线程资源去执行

InsttoolCacheScheduler_QuartzSchedulerThread线程交给它的调度任务(也就是JobRunShell)。

JBossLifeThread

Jboss

Jboss主线程启动成功,应用程序部署完毕之后将JBossLifeThread线程实例化并且start,JBossLifeThread线程启动成 功之后就处于等待状态,以保持Jboss Java进程处于存活中。 所得比较通俗一点,就是Jboss启动流程执行完毕之后,为什么没有结束?就是因为有这个线程hold主了它。牛b吧~~

JBoss System Threads(1)-1

Jboss

该线程是一个socket服务,默认端口号为:1099。主要用于接收外部naming service(Jboss  JNDI)请求。

JCA PoolFiller

Jboss

该线程主要为JBoss内部提供连接池的托管。 简单介绍一下工作原理:

Jboss内部凡是有远程连接需求的类,都需要实现ManagedConnectionFactory接口,例如需要做JDBC连接的

XAManagedConnectionFactory对象,就实现了该接口。然后将XAManagedConnectionFactory对象,还有其 它信息一起包装到InternalManagedConnectionPool对象里面,接着将 InternalManagedConnectionPool交给PoolFiller对象里面的列队进行管理。   JCA PoolFiller线程会定期判断列队内是否有需要创建和管理的InternalManagedConnectionPool对象,如果有的话,则调用 该对象的fillToMin方法,触发它去创建相应的远程连接,并且将这个连接维护到它相应的连接池里面去。

JDWP Event Helper Thread

JVM

JDWP是通讯交互协议,它定义了调试器和被调试程序之间传递信息的格式。它详细完整地定义了请求命令、回应数据和错误代码,保证了前端和后端的 JVMTI和JDI的通信通畅。 该线程主要负责将JDI事件映射成JVMTI信号,以达到调试过程中操作JVM的目的。

JDWP Transport Listener: dt_socket

JVM

该线程是一个Java Debugger的监听器线程,负责受理客户端的debug请求。通常我们习惯将它的监听端口设置为8787。

Low Memory Detector

JVM

这个线程是负责对可使用内存进行检测,如果发现可用内存低,分配新的内存空间。

process reaper

JVM

该线程负责去执行一个OS命令行的操作。

Reference Handler

JVM

JVM在创建main线程后就创建Reference Handler线程,其优先级最高,为10,它主要用于处理引用对象本身(软引用、弱引用、虚引用)的垃圾回收问题。

Surrogate Locker Thread (CMS)

JVM

这个线程主要用于配合CMS垃圾回收器使用,它是一个守护线程,其主要负责处理GC过程中,Java层的Reference(指软引用、弱引用等等)与 jvm内部层面的对象状态同步。这里对它们的实现稍微做一下介绍:这里拿WeakHashMap做例子,将一些关键点先列出来(我们后面会将这些关键点全 部串起来):

1. 我们知道HashMap用Entry[]数组来存储数据的,WeakHashMap也不例外,内部有一个Entry[]数组。

2.   WeakHashMap的Entry比较特殊,它的继承体系结构为Entry->WeakReference->Reference。

3.  Reference里面有一个全局锁对象:Lock,它也被称为pending_lock.   注意:它是静态对象。

4.       Reference 里面有一个静态变量:pending。

5.  Reference 里面有一个静态内部类:ReferenceHandler的线程,它在static块里面被初始化并且启动,启动完成后处于wait状态,它在一个Lock同步锁模块中等待。

6. 另外,WeakHashMap里面还实例化了一个ReferenceQueue列队,这个列队的作用,后面会提到。

7. 上面关键点就介绍完毕了,下面我们把他们串起来。

假设,WeakHashMap对象里面已经保存了很多对象的引用。JVM在进行CMS GC的时候,会创建一个ConcurrentMarkSweepThread(简称CMST)线程去进行 GC,ConcurrentMarkSweepThread线程被创建的同时会创建一个SurrogateLockerThread(简称SLT)线程并 且启动它,SLT启动之后,处于等待阶段。CMST开始GC时,会发一个消息给SLT让它去获取Java层Reference对象的全局锁:Lock。直 到CMS GC完毕之后,JVM会将WeakHashMap中所有被回收的对象所属的WeakReference容器对象放入到Reference的pending 属性当中(每次GC完毕之后,pending属性基本上都不会为null了),然后通知SLT释放并且notify全局锁:Lock。此时激活了 ReferenceHandler线程的run方法,使其脱离wait状态,开始工作了。ReferenceHandler这个线程会将pending中 的所有WeakReference对象都移动到它们各自的列队当中,比如当前这个WeakReference属于某个WeakHashMap对象,那么它 就会被放入相应的ReferenceQueue列队里面(该列队是链表结构)。当我们下次从WeakHashMap对象里面get、put数据或者调用 size方法的时候,WeakHashMap就会将ReferenceQueue列队中的WeakReference依依poll出来去和Entry[] 数据做比较,如果发现相同的,则说明这个Entry所保存的对象已经被GC掉了,那么将Entry[]内的Entry对象剔除掉。

taskObjectTimerFactory

JVM

顾名思义,该线程就是用来执行任务的。当我们把一个认为交给Timer对象,并且告诉它执行时间,周期时间后,Timer就会将该任务 放入任务列队,并且通知taskObjectTimerFactory线程去处理任务,taskObjectTimerFactory线程会将状态为取消 的任务从任务列队中移除,如果任务是非重复执行类型的,则在执行完该任务后,将它从任务列队中移除,如果该任务是需要重复执行的,则计算出它下一次执行的 时间点。

VM Periodic Task Thread

JVM

该线程是JVM周期性任务调度的线程,它由WatcherThread创建,是一个单例对象。该线程在JVM内使用得比较频繁,比如:定期的内存监控、JVM运行状况监控,还有我们经常需要去执行一些jstat这类命令查看gc的情况,如下:

jstat -gcutil 23483 250 7  这个命令告诉jvm在控制台打印PID为:23483的gc情况,间隔250毫秒打印一次,一共打印7次。

VM Thread

JVM

这个线程就比较牛b了,是jvm里面的线程母体,根据hotspot源码(vmThread.hpp)里面的注释,它是一个单例的对象 (最原始的线程)会产生或触发所有其他的线程,这个单个的VM线程是会被其他线程所使用来做一些VM操作(如,清扫垃圾等)。

在 VMThread的结构体里有一个VMOperationQueue列队,所有的VM线程操作(vm_operation)都会被保 存到这个列队当中,VMThread本身就是一个线程,它的线程负责执行一个自轮询的loop函数(具体可以参考:VMThread.cpp里面的 void VMThread::loop()),该loop函数从VMOperationQueue列队中按照优先级取出当前需要执行的操作对象 (VM_Operation),并且调用VM_Operation->evaluate函数去执行该操作类型本身的业务逻辑。

ps:VM操作类型被定义在vm_operations.hpp文件内,列举几个:ThreadStop、ThreadDump、 PrintThreads、GenCollectFull、GenCollectFullConcurrent、CMS_Initial_Mark、 CMS_Final_Remark…..有兴趣的同学,可以自己去查看源文件。

还有一些线程,下次有时间再整理吧,希望对大家有帮助。

JVM 内部运行线程介绍的更多相关文章

  1. jvm内部现成运行

    hi,all 最近抽时间把JVM运行过程中产生的一些线程进行了整理,主要是围绕着我们系统jstack生成的文件为参照依据.  前段时间因为系统代码问题,造成性能瓶颈,于是就dump了一份stack出来 ...

  2. java笔记--关于多线程如何查看JVM中运行的线程

    查看JVM中的线程 --如果朋友您想转载本文章请注明转载地址"http://www.cnblogs.com/XHJT/p/3890280.html "谢谢-- ThreadGrou ...

  3. java jstack dump 线程 介绍 解释

    最近抽时间把JVM运行过程中产生的一些线程进行了整理,主要是围绕着我们系统jstack生成的文件为参照依据.  前段时间因为系统代码问题,造成性能到了天花板,于是就dump了一份stack出来进行分析 ...

  4. JVM内部原理

    这篇文章详细描述了Java虚拟机的内在结构.下面这张图来自<The Java Virtual Machine Specification Java SE 7 Edition>,它展示了一个 ...

  5. JVM 内部原理(六)— Java 字节码基础之一

    JVM 内部原理(六)- Java 字节码基础之一 介绍 版本:Java SE 7 为什么需要了解 Java 字节码? 无论你是一名 Java 开发者.架构师.CxO 还是智能手机的普通用户,Java ...

  6. JVM 内部原理(四)— 基本概念之 JVM 结构

    JVM 内部原理(四)- 基本概念之 JVM 结构 介绍 版本:Java SE 7 每位使用 Java 的程序员都知道 Java 字节码在 Java 运行时(JRE - Java Runtime En ...

  7. JVM 内部原理(五)— 基本概念之 Java 虚拟机官方规范文档,第 7 版

    JVM 内部原理(五)- 基本概念之 Java 虚拟机官方规范文档,第 7 版 介绍 版本:Java SE 7 每位使用 Java 的程序员都知道 Java 字节码在 Java 运行时(JRE - J ...

  8. JVM 内部原理(一)— 概述

    JVM 内部原理(一)- 概述 介绍 版本:Java SE 7 图中显示组件将会从两个方面分别解释.第一部分涵盖线程独有的组件,第二部分涵盖独立于线程的组件(即线程共享组件). 目录 线程独享(Thr ...

  9. Windows程序内部运行机制 转自http://www.cnblogs.com/zhili/p/WinMain.html

    一.引言 要想熟练掌握Windows应用程序的开发,首先需要理解Windows平台下程序运行的内部机制,然而在.NET平台下,创建一个Windows桌面程序,只需要简单地选择Windows窗体应用程序 ...

随机推荐

  1. IBatis.Net使用总结(二)-- IBatis返回DataTable/DataSet(网上例子的集合)

    IBatis返回DataTable,DataSet ibatis.net QueryForDataTable 完整的为ibatis.net 引入datatable支持要改动很多地方,所以描述的是最小化 ...

  2. cve-2015-5199漏洞分析

    继续之前hackteam的flash漏洞,这次的对象为cve-2015-5199,遂做一下记录. 首先,在该exp中TryExpl函数为漏洞的触发函数,该函数也为本次调试的主要对象,函数的开始首先创建 ...

  3. one_person年轻的程序员

    回顾大学三年,通过良师的教导和自身的刻苦学习,我已初步掌握如何运用计算机编程,也养成了认真对待学习和工作的好习惯! 在思想品德上,本人有良好道德修养,并有坚定的政治方向.我热爱祖国,热爱人民,遵纪守法 ...

  4. python安装第三方类库的方法

    1.先到官网 http://pypi.python.org/pypi/setuptools 下载setuptools.exe文件并安装 点击 ez_setup.py进入, 并将内容复制下来, 保存为本 ...

  5. BZOJ2087 : [Poi2010]Sheep

    一条边能连上当且仅当它没有经过任何点,并且两边的点都是偶数个. 枚举原点,通过极角排序求出哪些边是合法的,然后区间DP即可. 时间复杂度$O(nm\log m+n^3)$. #include<c ...

  6. Query Mobile学习笔记

    1.获取jQuery mobile 文件,访问jQuerymobile网站下载 (貌似使用jquery mobile后,jquery会自动在网页中添加一些class类,第一次知道的我是被吓呆的!!) ...

  7. 【Telerik】<telerik:RadGridView/>控件的使用

    学习Telerik第三方控件中的WPF时,对于RadGridView控件做的一些记录. AutoGenerateColumns:启动时是否生成列 ShowGroupPanel:是否显示表格的分组名称 ...

  8. C#中(int)、int.Parse()、int.TryParse()和Convert.ToInt32()的区别

    转自:http://www.cnblogs.com/leolis/p/3968943.html 在编程过程中,数据转换是经常要用到的,C#中数据转换的方法很多,拿将目标对象转换为 整型(int)来讲, ...

  9. 多线程相关------互斥量Mutex

    互斥量(Mutex) 互斥量是一个可以处于两态之一的变量:解锁和加锁.只有拥有互斥对象的线程才具有访问资源的权限.并且互斥量可以用于不同进程中的线程的互斥访问. 相关函数: CreateMutex用于 ...

  10. ajax 通用方法,从thinkphp中拔出来的

    <?php /** * 设置页面输出的CONTENT_TYPE和编码 * @access public * @param string $type content_type 类型对应的扩展名 * ...