首页 文章 精选 留言 我的

精选列表

搜索[优化],共10000篇文章
优秀的个人博客,低调大师

优化技术专题-虚拟机线程问题分析(1)jstack分析线程状态

前提概要 学习研究thread dump文件是一种很不错的能力哦,因为它可以帮助我们在危急关头去解决和分析问题,接下来,就让我们开始分析和研究一下jstack dump文件吧。 jstack Dump日志文件中的线程状态 dump文件里,值得关注的线程状态 死锁,Deadlock(重点关注) 执行中,Runnable 等待资源,Waiting on condition(重点关注) 等待获取监视器,Waiting on monitor entry(重点关注) 暂停,Suspended 对象等待中,Object.wait() 或 TIMED_WAITING 阻塞,Blocked(重点关注) 停止,Parked 下面我们先从第一个例子开始分析,然后再列出不同线程状态的含义以及注意事项,最后再补充两个实例。 综合示范一:Waiting to lock 和 Blocked 实例如下: "RMI TCP Connection(267865)-172.16.5.25" daemon prio=10 tid=0x00007fd508371000 nid=0x55ae waiting for monitor entry [0x00007fd4f8684000] java.lang.Thread.State: BLOCKED (on object monitor) at org.apache.log4j.Category.callAppenders(Category.java:201) - waiting to lock <0x00000000acf4d0c0> (a org.apache.log4j.Logger) at org.apache.log4j.Category.forcedLog(Category.java:388) at org.apache.log4j.Category.log(Category.java:853) at org.apache.commons.logging.impl.Log4JLogger.warn(Log4JLogger.java:234) at com.tuan.core.common.lang.cache.remote.SpyMemcachedClient.get(SpyMemcachedClient.java:110) 1)线程状态是 Blocked,阻塞状态。说明线程等待资源超时! 2)“ waiting to lock <0x00000000acf4d0c0>”指,线程在等待给这个 0x00000000acf4d0c0 地址上锁(英文可描述为:trying to obtain 0x00000000acf4d0c0 lock)。 3)在 dump 日志里查找字符串 0x00000000acf4d0c0,发现有大量线程都在等待给这个地址上锁。如果能在日志里找到谁获得了这个锁(如locked < 0x00000000acf4d0c0 >),就可以顺藤摸瓜了。 4)“waiting for monitor entry”说明此线程通过 synchronized(obj) {……} 申请进入了临界区,从而进入了下图1中的“Entry Set”队列,但该 obj 对应的 monitor 被其他线程拥有,所以本线程在 Entry Set 队列中等待。 5)第一行里,"RMI TCP Connection(267865)-172.16.5.25"是 Thread Name 。tid指Java Thread id。nid指native线程的id。prio是线程优先级。[0x00007fd4f8684000]是线程栈起始地址。 Dump文件中的线程状态含义及注意事项 含义如下所示: Deadlock:死锁线程,多个线程调用间,进入相互资源占用,导致一直等待无法释放的情况。 Runnable:一般指该线程正在执行状态中,该线程占用了资源,正在处理某个请求,有可能正在传递SQL到数据库执行,有可能在对某个文件操作,有可能进行数据类型等转换。 Waiting on condition:等待资源,或等待某个条件的发生。具体原因需结合 stacktrace来分析。 如果堆栈信息明确是应用代码,则证明该线程正在等待资源。一般是大量读取某资源,且该资源采用了资源锁的情况下,线程进入等待状态,等待资源的读取。又或者,正在等待其他线程的执行等。 如果发现有大量的线程都在处在 Wait on condition,从线程 stack看,正等待网络读写,这可能是一个网络瓶颈的征兆。因为网络阻塞导致线程无法执行。 一种情况是网络非常忙,几乎消耗了所有的带宽,仍然有大量数据等待网络读写; 另一种情况也可能是网络空闲,但由于路由等问题,导致包无法正常的到达。 另外一种出现 Wait on condition的常见情况是该线程在 sleep,等待 sleep的时间到了时候,将被唤醒。 Blocked:线程阻塞,是指当前线程执行过程中,所需要的资源长时间等待却一直未能获取到,被容器的线程管理器标识为阻塞状态,可以理解为等待资源超时的线程。 Waiting for monitor entry 和 in Object.wait():Monitor是 Java中用以实现线程之间的互斥与协作的主要手段,它可以看成是对象或者 Class的锁。每一个对象都有,也仅有一个 monitor。从下图1中可以看出,每个 Monitor在某个时刻,只能被一个线程拥有,该线程就是 “Active Thread”,而其它线程都是 “Waiting Thread”,分别在两个队列 “ Entry Set”和 “Wait Set”里面等候。在 “Entry Set”中等待的线程状态是 “Waiting for monitor entry”,而在 “Wait Set”中等待的线程状态是 “in Object.wait()”。 图1 A Java Monitor 综合示范二:Waiting on condition 和 TIMED_WAITING 实例如下: "RMI TCP Connection(idle)" daemon prio=10 tid=0x00007fd50834e800 nid=0x56b2 waiting on condition [0x00007fd4f1a59000] java.lang.Thread.State: TIMED_WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x00000000acd84de8> (a java.util.concurrent.SynchronousQueue$TransferStack) at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:198) at java.util.concurrent.SynchronousQueue$TransferStack.awaitFulfill(SynchronousQueue.java:424) at java.util.concurrent.SynchronousQueue$TransferStack.transfer(SynchronousQueue.java:323) at java.util.concurrent.SynchronousQueue.poll(SynchronousQueue.java:874) at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:945) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:907) at java.lang.Thread.run(Thread.java:662) 1)“TIMED_WAITING (parking)”中的 timed_waiting 指等待状态,但这里指定了时间,到达指定的时间后自动退出等待状态;parking指线程处于挂起中。 2)“waiting on condition”需要与堆栈中的“parking to wait for <0x00000000acd84de8> (a java.util.concurrent.SynchronousQueue$TransferStack)”结合来看。首先,本线程肯定是在等待某个条件的发生,来把自己唤醒。其次,SynchronousQueue 并不是一个队列,只是线程之间移交信息的机制,当我们把一个元素放入到 SynchronousQueue 中时必须有另一个线程正在等待接受移交的任务,因此这就是本线程在等待的条件。 3)别的就看不出来了。 综合示范三:in Obejct.wait() 和 TIMED_WAITING 实例如下: "RMI RenewClean-[172.16.5.19:28475]" daemon prio=10 tid=0x0000000041428800 nid=0xb09 in Object.wait() [0x00007f34f4bd0000] java.lang.Thread.State: TIMED_WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x00000000aa672478> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:118) - locked <0x00000000aa672478> (a java.lang.ref.ReferenceQueue$Lock) at sun.rmi.transport.DGCClient$EndpointEntry$RenewCleanThread.run(DGCClient.java:516) at java.lang.Thread.run(Thread.java:662) 1)“TIMED_WAITING (on object monitor)”,对于本例而言,是因为本线程调用了 java.lang.Object.wait(long timeout) 而进入等待状态。 2)“Wait Set”中等待的线程状态就是“ in Object.wait() ”。当线程获得了 Monitor,进入了临界区之后,如果发现线程继续运行的条件没有满足,它则调用对象(一般就是被 synchronized 的对象)的 wait() 方法,放弃了 Monitor,进入 “Wait Set”队列。只有当别的线程在该对象上调用了 notify() 或者 notifyAll() ,“ Wait Set”队列中线程才得到机会去竞争,但是只有一个线程获得对象的 Monitor,恢复到运行态。 3)RMI RenewClean 是 DGCClient 的一部分。DGC 指是 Distributed GC,即分布式垃圾回收。 4)请注意,是先 locked <0x00000000aa672478>,后 waiting on <0x00000000aa672478>,即,线程的执行中,先用 synchronized 获得了这个对象的 Monitor(对应于 locked <0x00000000aa672478> );当执行到** lock.wait(timeout);**,线程就放弃了 Monitor 的所有权,进入“Wait Set”队列(对应于 waiting on <0x00000000aa672478> )。 之所以先锁再等同一个对象,请看下面它的代码实现: static private class Lock { }; private Lock lock = new Lock(); public Reference<? extends T> remove(long timeout) { synchronized (lock) { Reference<? extends T> r = reallyPoll(); if (r != null) return r; for (;;) { lock.wait(timeout); r = reallyPoll(); …… } } 5)从堆栈信息看,是正在清理 remote references to remote objects ,引用的租约到了,分布式垃圾回收在逐一清理呢。

优秀的个人博客,低调大师

Proxy-Go 控制面板 v2.8 发布,优化创建服务API!

Proxy-Go 控制面板 ProxyAdmin是强大的代理服务工具snail007/goproxy的控制面板,运行了它,一秒让你的服务器变为强大的代理服务器,友好的交互界面,小白也能轻松上手,让你用起来得心应手,心情舒畅。 更新内容: 1.新增在添加代理服务后,返回的json里面加上了服务ID,方便通过API控制。 升级提示: 1.备份配置目录数据文件。 2.卸载旧版本,重新安装新版本。 3.使用备份的数据库文件覆盖配置目录的数据文件。 功能预览 展现客户端参数 实时日志 参数文件管理 调试模式 下载地址: Gitee Github

优秀的个人博客,低调大师

【性能优化】纳尼?内存又溢出了?!是时候总结一波了!!

点击上方蓝色“冰河技术”,关注并选择“设为星标” 持之以恒,贵在坚持,每天进步一点点! 作者个人研发的在高并发场景下,提供的简单、稳定、可扩展的延迟消息队列框架,具有精准的定时任务和延迟队列处理功能。自开源半年多以来,已成功为十几家中小型企业提供了精准定时调度方案,经受住了生产环境的考验。为使更多童鞋受益,现给出开源框架地址: https://github.com/sunshinelyz/mykit-delay PS: 欢迎各位Star源码,也可以pr你牛逼哄哄的代码。 写在前面 相信小伙伴们在平时工作的过程中,或多或少都会遇到一个场景:内存溢出。如果你没有遇到过这个场景,那就说明你是个假的程序员。哈哈,开个玩笑,平时工作过程中,我们确实会遇到这个问题。今天,我就将平时工作过程中遇到的内存溢出情况做个简单的总结,以通俗易懂的代码案例的形式直观的分享给大家。希望能够为小伙伴们带来实质性的帮助。 案例介绍 这里,我将在平时工作过程中总结的内存溢出的情况,以代码案例的形式直观的分享给大家,希望能够为小伙伴们带来实质性的帮助。 接下来,我们就以代码案例的形式来分析各种内存溢出的情况。 定义主类结构 首先,我们创建一个类叫做BlowUpJVM,所有的案例实验都是基于这个类进行。 publicclassBlowUpJVM{} 栈深度溢出 publicstaticvoidtestStackOverFlow(){BlowUpJVM.testStackOverFlow();} 栈不断递归,而且没有处理,所以虚拟机栈就不断深入不断深入,栈深度就这样溢出了。 永久代内存溢出 publicstaticvoidtestPergemOutOfMemory1(){//方法一失败List<String>list=newArrayList<String>();while(true){list.add(UUID.randomUUID().toString().intern());}} 打算把String常量池堆满,没想到失败了,JDK1.7后常量池放到了堆里,也能进行垃圾回收了。 然后换种方式,使用cglib,用Class把老年代取堆满 publicstaticvoidtestPergemOutOfMemory2(){try{while(true){Enhancerenhancer=newEnhancer();enhancer.setSuperclass(OOM.class);enhancer.setUseCache(false);enhancer.setCallback(newMethodInterceptor(){@OverridepublicObjectintercept(Objectobj,Methodmethod,Object[]args,MethodProxyproxy)throwsThrowable{returnproxy.invokeSuper(obj,args);}});enhancer.create();}}catch(Exceptione){e.printStackTrace();}} 虚拟机成功内存溢出了,那JDK动态代理产生的类能不能溢出呢? publicstaticvoidtestPergemOutOfMemory3(){while(true){finalOOMoom=newOOM();Proxy.newProxyInstance(oom.getClass().getClassLoader(),oom.getClass().getInterfaces(),newInvocationHandler(){publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{Objectresult=method.invoke(oom,args);returnresult;}});}} 事实表明,JDK动态代理差生的类不会造成内存溢出,原因是:JDK动态代理产生的类信息,不会放到永久代中,而是放在堆中。 本地方法栈溢出 publicstaticvoidtestNativeMethodOutOfMemory(){intj=0;while(true){Printer.println(j++);ExecutorServiceexecutors=Executors.newFixedThreadPool(50);inti=0;while(i++<10){executors.submit(newRunnable(){publicvoidrun(){}});}}} 这个的原理就是不断创建线程池,而每个线程池都创建10个线程,这些线程池都是在本地方法区的,久而久之,本地方法区就溢出了。 JVM栈内存溢出 publicstaticvoidtestStackOutOfMemory(){while(true){Threadthread=newThread(newRunnable(){publicvoidrun(){while(true){}}});thread.start();}} 线程的创建会直接在JVM栈中创建,但是本例子中,没看到内存溢出,主机先挂了,不是JVM挂了,真的是主机挂了,无论在mac还是在windows,都挂了。 温馨提示,这个真的会死机的。 堆溢出 publicstaticvoidtestOutOfHeapMemory(){List<StringBuffer>list=newArrayList<StringBuffer>();while(true){StringBufferB=newStringBuffer();for(inti=0;i<10000;i++){B.append(i);}list.add(B);}} 不断往堆中塞新增的StringBuffer对象,堆满了就直接溢出了。 重磅福利 微信搜一搜【冰河技术】微信公众号,关注这个有深度的程序员,每天阅读超硬核技术干货,公众号内回复【PDF】有我准备的一线大厂面试资料和我原创的超硬核PDF技术文档,以及我为大家精心准备的多套简历模板(不断更新中),希望大家都能找到心仪的工作,学习是一条时而郁郁寡欢,时而开怀大笑的路,加油。如果你通过努力成功进入到了心仪的公司,一定不要懈怠放松,职场成长和新技术学习一样,不进则退。如果有幸我们江湖再见! 另外,我开源的各个PDF,后续我都会持续更新和维护,感谢大家长期以来对冰河的支持!! 写在最后 如果你觉得冰河写的还不错,请微信搜索并关注「 冰河技术 」微信公众号,跟冰河学习高并发、分布式、微服务、大数据、互联网和云原生技术,「 冰河技术 」微信公众号更新了大量技术专题,每一篇技术文章干货满满!不少读者已经通过阅读「 冰河技术 」微信公众号文章,吊打面试官,成功跳槽到大厂;也有不少读者实现了技术上的飞跃,成为公司的技术骨干!如果你也想像他们一样提升自己的能力,实现技术能力的飞跃,进大厂,升职加薪,那就关注「 冰河技术 」微信公众号吧,每天更新超硬核技术干货,让你对如何提升技术能力不再迷茫! 留言区 本文分享自微信公众号 - 冰河技术(hacker-binghe)。如有侵权,请联系 support@oschina.cn 删除。本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

资源下载

更多资源
Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册