精华内容
下载资源
问答
  • 在测试过程中,部门同事反映:在不同的异常信息之间多次切换,会导致网页崩溃。在案发现场打开 chrome 的任务管理器,看到这个页面内存占用已经达到9.7G,初步怀疑页面存在泄漏。验证猜测打开 d...

    编者按:本文转载自掘金专栏,由作者 舍掉英熊1 授权奇舞周刊转载。

    背景

    项目是利用vue框架开发的公司内部的异常监控系统,用于显示java程序运行时的异常信息,包括执行堆栈、代码、变量等信息显示。

    在测试过程中,部门同事反映:在不同的异常信息之间多次切换,会导致网页崩溃。在案发现场打开 chrome 的任务管理器,看到这个页面内存占用已经达到了9.7G,初步怀疑页面存在泄漏。

    验证猜测

    1. 打开 devtool -> performance,开始记录页面性能

    2. 执行页面上切换其它异常信息的操作(页面最有可能引起内存泄漏的操作)

    3. 查看性能分析结果

    1851014ff74c82e936ccfd95e51c6221.png

    可以看到Nodes、Listeners、JS Head(memory) 的阶梯式增长,中间的增长节点对应就是每一次操作。很显然这个操作会引起内存的持续增长,最终发生内存溢出也是顺理成章了。

    问题分析

    在动手之前,我已知的信息有:

    1.从performace工具,可以看到 JS Heap、Nodes、Listeners 的累积增长 2.以上三点,实际上存在依赖关系:

    (1)、变量引用DOM

    (2)、子级DOM不能释放,会导致父级也不会被释放

    (3)、如果DOM能被正常GC, 对DOM的事件监听器也会自动移除

    3.可以使用 devtool->memory -> take snapshot 收集内存快照并分析工具文档(https://developers.google.cn/web/tools/chrome-devtools/memory-problems/heap-snapshots?hl=zh-tw);

    简单认识一下 snapshot

    268dc25036cd86f051062d19cbca2088.png

    嗯,内容有点多,但是也还算清晰:

    1.按数据类型进行统计,可以看到一些内建对象、 Vue 对象、自定义对象(比如 Exception、StackFrame 等)、Detached Element、EventListener等等。 2.纵坐标有Distance, shallow size, Retained Size, 可以不准确理解为:

    Distance:到root的引用距离

    Shallow size:对象本身的大小,不包含它引用的数据的大小

    Retained size:对象自身以及所有引用的大小,就是对象总共占用的内存 (如果它引用的对象不被其他不可回收的对象引用的时候。用google开发者网站的描述叫:将对象本身连同其无法从GC根到达的相关对象一起删除后释放的内存大小)

    3.下面的 retainers 面板,可以看到变量的具体引用路径、在哪里被创建、以及在哪里被使用

    5d2d82d9c2cf40e9f4f91845eb500c85.png

    定位问题:找到那些被引用本该被释放,但实际没有的释放的对象

    1.执行引起内存泄漏的操作

    该操作的核心代码大致是这样的

    75b554f14e5a0350f6262ddd670ba340.png  

    主要功能是,每次执行setEvent,都将 this.exception 指向新的实例,并交给页面进行数据展示,而之前被this.exception引用的对象,应该被释放。

    2.重新收集新的内存快照信息

    3.找出差异:将视图改为差异视图

    56e2928d0e35dd6129d7827ab8f3673e.png

    从图上可以看到在步骤1之后,出现了很多新增的对象,但是删除的对象是0。

    以 Exception对象为例,按照步骤1的代码逻辑,新对象建立,旧对象被释放,Delta 应该为零。所以可以明确知道,这里是一个问题。不过这里点开查看变量的引用详情,并没得到太多有用的信息,只知道被哪个 vue component 引用了,但是component 太多,不太好定位。

    查看 Listenters, 我看到的画风基本是这样的:

    e8d747556b33fba68798dd2cac2bfb8e.png

    跟预期的结果一致,都是由于一些 Nodes 没有被释放导致的。不过确实没有得到太多方便分析的信息。

    另外查看Nodes相关的信息,搜索 Detached, 可以看到一些 Detatched HTMLDivElement等等类似的对象,也就是在内存中但是没有在页面进行渲染的元素

    我找了一个detla比较小的、节点功能也清晰(就是用来在页面中进行代码高亮的元素)的 Detatched HTMLPreElement 进行分析:

    4eb50e3a1fdf5e967adc0ea82b9233c7.png

    可以看到实际引用关系为 div <= div <= div <= vue component <= var-hover <= events <= ... $platform.event...

    在这里 $platform.event 是由平台 + 模块的架构设计中,平台提供的事件 api, 用于全局的事件通信。

    最终将以上引用关系进行翻译:由平台提供的事件 $platform.event (全局,绑定的事件函数不会被自动释放),绑定了一个叫做 var-hover 的事件 => var-hover 的事件函数中引用了一个 vue-component => component 的$el属性 引用了某个Dom => Dom的父级被子级引用导致不能被GC。

    可以看看 var-hover 的代码:

    4a15269a414f378ac8e9efcbaef74f90.png

    var-hover 绑定了一个匿名函数(基本上也可以知道,没有给这个事件没有写过解绑操作),然后匿名函数中使用了 this, 也就是当前 vue component,这也导致了被这个 component 引用的对象都不能被GC。

    所以祸根基本上找到了,接下来要做的就是:修复 -> 重新验证

    修复

    1.第一次简单修改:在 beforeDestroy 中进行事件解绑,当时验证确认内存溢出问题已解决 2.手动解绑这是个大坑,很多地方很多人在编写代码的时候,真不一定有这个好习惯。所有也就有了现在的处理方案:对平台接口进行改造,支持事件的基于组件的自动解绑。代码如下:

    2a7c3ebc30fdf8d4232e071ce1ac66ab.png

    这就是$platform.event 的实际实现

    var-hover的事件绑定如下

    b87e6bca9375531d0a05bd4301deee36.png

    移除了 beforeDestroy 钩子,业务层看起来也好多了。

    验证

    1.利用 performance 功能,多次进行之前导致内存溢出的操作,得到结果如下

    9d55bc12018c0568675359219f640183.png

    这里的每次峰值,就是刚执行进行操作时进行内存分配的结果,之后每次执行,并没有出现内存及 Nodes, Listensers 的累积

    再次对比一下修正之前的性能分析结果

    1851014ff74c82e936ccfd95e51c6221.png

    可怕的楼梯。。。

    2.顺便再 memory 面板中出现了什么变化

    bb10ee6db1a2318d180dfb3aef888fb8.png

    多了一个 StackFrameVar 以及一些为了呈现这个 StackFrameVar 对象多出来的一些EventListener、Observer等,这是由于两次呈现的数据本身不一样导致的,属于正常情况

    Exception、 等很多对象的 Delta 已经为0了(按 Delta倒序排列的)

    其它说明

    以上分析图是写这篇文章过程中,回写部分代码之后实时分析的,相对而言没有实际调试时处理得那么细致。实际调试过程中还做了其它操作:

    1.隐私窗口,禁用所有扩展(避免影响内存分析) 2.关闭开发模式HMR功能,因为 VUE_HOT_RELOAD 也会产生一层引用,我并不能完全信任它 3.使用模拟数据,每次执行操作,都会渲染一样的可被人工计算清楚(知道哪个类会产生多少实例)的数据 4.performance 过程中手动GC

    通过以上方式是为了提供一个完全纯净可控的分析环境。

    关于奇舞周刊

    《奇舞周刊》是360公司专业前端团队「奇舞团」运营的前端技术社区。关注公众号后,直接发送链接到后台即可给我们投稿。

    f6706f81b1e078026993ee674cc8231c.png

    展开全文
  • 最困难的事情就是认识自己!个人网站 ,欢迎访问!前言:最近,测试部门的...哈哈,开玩笑的,像我这么英俊的人,领导怎么会忍心批评我呢,哼,我把这个问题马上解决掉,都不会让领导知道的!简单说下程序部署情况...

    最困难的事情就是认识自己!

    个人网站 ,欢迎访问!

    前言:

    最近,测试部门的同事找到我,说他们测试时,没一会就发现服务接口请求一直无响应,Tomcat跟死掉了一样,也没有返回任何的错误响应,说让我赶紧排查下;听完后,我瞬间激灵了下,妹的,最近老是出问题,领导都要给我开批评大会了。哈哈,开玩笑的,像我这么英俊的人,领导怎么会忍心批评我呢,哼,我把这个问题马上解决掉,都不会让领导知道的!

    简单说下程序部署情况:tomcat + oracle

    排查过程:

    排查时,可以使用命令进行排查,也可以使用可视化监控工具;例如使用使用JDK自带的 jvisualvm.exe 监控工具。

    命令排查过程:

    1、请求服务无响应,首先看看tomcat是否是真的挂掉了:

    命令: ps -ef | grep tomcat

    通过上面的命令查看tomcat运行着;执行结果如下:

    1460000037517132

    注意: 如果此服务器中运行着多个tomcat,需要查看下图中画框的地方运行的tomcat地址是否正确;

    通过命令查看发现,tomcat正常运行着,那么这就是处于假死状态,下面接着排查。

    2、查看http请求是否到达了tomcat:

    通过查看 tomcat 的 logs 目录下的 localhost_access_log 日志文件中 请求记录;

    命令: tail -100f localhost_access_log

    通过上面的命令查看实时的日志,执行完上面的查看日志的命令后,然后再次请求下程序,在日志中并没有发现请求记录,说明tomcat处于一种假死状态,下面接着排查。

    3、查看tomcat的JVM的GC情况:

    查看GC情况,是否由于频繁的GC,长时间的GC,导致程序出现长时间的卡顿,最终导致请求来不及处理,进入队列中进行等待,调用方长时间得不到响应,造成tomcat假死状态;

    命令:jstat -gc pid time count

    例如: jstat -gc 71129 1000 5 监控 71129 这个进程JVM的GC情况,每隔1000ms 输出一次,共输出5次;

    1460000037517131

    命令执行结果参数解析:

    1460000037517133

    通过上面命令查看GC情况,发现垃圾回收也不频繁,并且进行GC的时间也不长,应该不是GC的原因。

    4、查看tomcat的JVM的堆情况:

    查看堆内存的情况,是否存在堆内存溢出 导致tomcat假死,无法为新请求分配堆内存资源;

    命令 : jmap -heap pid

    例子: jmap -heap 71129 71129是正在运行tomcat的进程号 ;

    1460000037517134

    通过命令执行结果得知,堆内存中可使用内存还很大,不会出现内存溢出的问题,所以也不是堆内存过小导致的tomcat假死。

    5、查看tomcat的 JVM线程情况:

    ①、使用 jstack 命令导出当前JVM的线程dump快照,然后看看dump中线程都在干什么?

    命令:jstack pid >> jvmThreadDump.log

    例子:jstack 71129 >> jvmThreadDump.log

    生成 71129 进程的 JVM的线程快照,并将快照内容重定向到 jvmThreadDump.log 文件中;

    注意:生成的 jvmThreadDump.log 在你当前执行命令的目录下。

    ②、接着使用命令 more 查看 jvmThreadDump.log 内容;

    命令 : more jvmThreadDump.log

    如果的dump文件太大的话,需要使用more 命令一点点看;执行完more 命令的话,再按 enter 回车键 一点点展示文件内容;

    ③、通过查看线程快照文件,发现很多线程的状态是 WAITING 等待状态;

    并且使用命令查看线程状态为 WAITING 的线程占总线程的比例:

    注意:tomcatDump.log 为生成的线程快照文件名称,记得改为自己设置的名称 ;

    count=`cat tomcatDump.log | grep java.lang.Thread.State | wc -l`; wait=`cat tomcatDump.log | grep WAITING | wc -l`; a=`echo | awk "{print $wait/$count*100}"`; echo "$a%"

    执行命令,得到结果 : 91.9786% ,发现九成多的线程处于等待状态;

    至此,找到了tomcat假死的原因,但是还需进一步确定 什么原因导致的大量线程一直等待?

    通过查看调用的服务接口代码得知,此接口业务逻辑中自己没设置任何的锁,所以应该不是自己写的代码的问题,但是此接口中涉及到了很多 JDBC数据库操作,那是不是数据库连接池中的连接不够用了呢?因为数据库连接属于竞争资源,如果连接池中的连接已经耗尽了,那么接下来的进行 JDBC的线程就需要进行wait 等待连接。

    6、查看与数据库建立的TCP连接情况:

    在上面发现,大量线程处于等待状态,而通过分析得知,可能是由于数据库连接池中的连接耗尽导致的,所以可以通过命令查看下,部署服务代码的服务器与数据库所在服务器建立的TCP连接数是否已经达到了配置的数据库连接池的最大连接数;

    命令:netstat -pan | grep 1521 | wc -l

    因为本文中使用的数据库是Oracle,所以 grep 搜索匹配的端口号是 1521;

    如果是mysql数据库则将端口号改为3306 即可, netstat -pan | grep 3306 | wc -l ;

    如果设置了自定义的数据库端口号,则改为自定义的端口号即可;

    通过命令查询到 已经使用的数据库的连接数为 6 个,那接着看下设置的数据库连接池最大连接数;

    数据源配置如下:

    type="javax.sql.DataSource"

    factory="com.alibaba.druid.pool.DruidDataSourceFactory"

    url="jdbc:oracle:thin:@192.168.3.125:1521:ora11g"

    driverClassName="oracle.jdbc.driver.OracleDriver"

    username="root"

    password="root"

    auth="Container"

    initialSize="2"

    maxActive="6"

    minIdle="3"

    maxWait="30000"

    timeBetweenEvictionRunsMillis="30000"

    minEvictableIdleTimeMillis="600000"

    maxEvictableIdleTimeMillis="900000"

    poolPreparedStatements="true"

    maxOpenPreparedStatements="20"

    validationQuery="select 1 from dual"

    testOnBorrow="false"

    testOnReturn="false"

    testWhileIdle="true"

    filters="wall,stat,log4j2"

    />

    通过查看数据源发现,连接池配置的最大连接数是 maxActive=”6″ ;发现目前程序中使用的连接数已达到最大值,那么后面再进行 JDBC 操作的线程将进入 等待状态 ,等待获取连接;

    至此,tomcat假死的排查过程就结束了,并且原因也找到了,就是数据库连接池中的连接耗尽了;所以,在后面测试中,需要在数据源中将最大连接数设置的大一些,并且也再进一步查看下代码,看看是否存在数据库连接使用完后没有进行关闭的问题。

    除了数据库连接池连接耗尽会导致tomcat假死外,还有一些其它的情况也会导致发生,例如: redis 连接池连接耗尽,或者是redis连接使用完不释放,最终导致redis连接耗尽。

    除了使用上面的命令进行问题排查外,也可以直接使用可视化监控工具进行排查,更加简便、直观。

    可视化监控工具排查

    使用 JDK 自带的 jvisualvm.exe 工具进行 JMX远程 可视化监控tomcat;

    jvisualvm.exe 位于 $JAVA_HOME/bin 目录下;

    1、使用JMX实现远程监控步骤:

    下面使用 JMX实现远程监控的内容参考自:jvisualvm远程监控tomcat

    ①、在 Tomcat 的 bin 目录下的 startup.sh 文件中的 倒数第一行上 加上如下内容:

    export CATALINA_OPTS="$CATALINA_OPTS

    -Dcom.sun.management.jmxremote

    -Djava.rmi.server.hostname=192.168.1.130

    -Dcom.sun.management.jmxremote.port=7003

    -Dcom.sun.management.jmxremote.ssl=false

    -Dcom.sun.management.jmxremote.authenticate=false"

    上面内容参数解析:

    -Dcom.sun.management.jmxremote 启用JMX远程监控

    -Djava.rmi.server.hostname=192.168.1.130 这是连接你的tomcat所在的服务器地址

    -Dcom.sun.management.jmxremote.port=7003 jmx连接端口

    -Dcom.sun.management.jmxremote.ssl=false 是否ssl加密

    -Dcom.sun.management.jmxremote.authenticate=false 远程连接需要密码认证

    在 startup.sh 文件中添加上上面的内容后,需要将tomcat重启下才会生效;

    ②、将 jvisualvm.exe 打开,界面如下:

    1460000037517136

    ③、在远程上右击,添加主机,输入服务器的ip:就是在 startup.sh 文件中添加内容中的hostname

    1460000037517135

    ④、在远程主机上右击,添加 JMX连接 ,手动在ip地址后面加上设置的jmx连接端口7003,然后点击确定即可:

    1460000037517138

    ⑤、通过上面的步骤,就已经完成了远程监控连接了,然后自己双击就能进行监控界面了:

    1460000037517137

    2、查看监视内容:

    1460000037517140

    通过查看监视画面得知,CPU、GC、堆Heap的情况都没有问题,那接着查看下线程的情况:

    1460000037517139

    点击上面图片中的 线程Dump 按钮,生成线程的快照,快照文件内容部分如下:

    1460000037517141

    通过查看快照文件内容发现,很多线程的状态的都是 WAITING 等待状态;

    接下来的分析排查过程就如上面的 命令排查过程 一样了。

    总结:

    上面的两种排查方式,本人比较推荐还是使用第一种 命令排查 ,因为很多的情况是不会让你修改配置文件进行远程监控的,即便使用监控工具看起来更加直观、简便;所以,平时需要记一些常用的排查命令,以备不时之需。

    由于本人水平有限,如有问题,敬请提出;

    ❤不要忘记留下你学习的足迹 [点赞 + 收藏 + 评论]嘿嘿ヾ

    一切看文章不点赞都是“耍流氓”,嘿嘿ヾ(◍°∇°◍)ノ゙!开个玩笑,动一动你的小手,点赞就完事了,你每个人出一份力量(点赞 + 评论)就会让更多的学习者加入进来!非常感谢! ̄ω ̄=

    展开全文
  • Linux下 解决Tomcat8不自动解压war包

    万次阅读 2018-11-28 11:30:27
    然后就想当然的将项目打成war包,直接拖到Tomcat的webapps下,但无论怎么重启项目就是不自动解压,后来查了各方面资料,删除这个文件那个文件的,差点把Tomcat崩溃了也没找到原因,最后看到讲一个将Tomcat里面配置...

    之前买了阿里云服务器后配置好了jdk,Tomcat,mysql等。然后就想当然的将项目打成war包,直接拖到Tomcat的webapps下,但无论怎么重启项目就是不自动解压,后来查了各方面资料,删除这个文件那个文件的,差点把Tomcat搞崩溃了也没找到原因,最后看到讲一个将Tomcat里面配置文件的文章我才发现
    在这里插入图片描述

    原来是tomcat/conf下server.xml里面的这行配置搞的鬼。
    其中:

    • appBase=“xxx/xxx” 表示项目的路径,有的默认为null,有的默认为webapps,你想要把项目放在哪就把这个路径改成哪
    • unpackWARs=“true” 是否自动解压war包
    • autoDeploy=“true” 是否在不重启下自动解压war包

    下面两个设置为true就好

    保存文件,将war包放到指定目录,重启就ok了

    展开全文
  • 1.服务器崩溃,指的是Tomcat程序崩溃,还是服务器系统崩溃? 答:都有可能。 所以一台服务器上部署多个Tomcat可以防止...怎么解决? 答:可以采取部分升级策略啊,先升级一部分,再升级另一部分。 或者采取夜间...

    1.服务器崩溃,指的是Tomcat程序崩溃,还是服务器系统崩溃?

    答:都有可能。

    所以一台服务器上部署多个Tomcat可以防止程序崩溃问题。但不能避免服务器崩溃,要避免服务器崩溃,就要采用服务器集群。

    2.那么多台Tomcat,也就是集群部署时。那怎么升级程序呢?如果升级了,升级过程中就不能访问网站了啊?怎么解决?

    答:可以采取部分升级策略啊,先升级一部分,再升级另一部分。

    或者采取夜间升级。比如凌晨4点

    3.临时访问量增加时,可以通过添加Tomcat服务器来提高并发量,活动取消时,再撤去一些Tomcat服务器。

    比如双11期间,阿里会增加1倍的服务器,双11结束后,再撤掉多余的服务器。回到正常水平。

     

    展开全文
  • 怎么解决eclipse报PermGen space异常的问题 最近使用eclipse做开发,使用的服务器是tomcat,但在启动时报Caused by: java.lang.OutOfMemoryError: PermGen space的异常。 这个错误很常见,于是配置tomcat下的...
  • javamail直接在eclipse下跑和在jetty下跑完全正常,但是放到tomcat下测试时,发现正文部分是乱码,标题...应该是这里要怎么设置,不过基本昨天试过十几种方法都不行,要崩溃了。不明白为什么在tomcat下不行,其他都行。
  • 相信很多用Tomcat做web容易的开发者都知道,Tomcat运行久以后,会崩溃掉,并且原有的关闭和启动脚本并不能使用,因为端口仍然占用。因此需要做几件事: 关闭Tomcat,检测端口占用,启动Tomcat检测Tomcat应用...
  • tomcat安装不成功问题

    2016-07-18 10:02:51
    工欲善其事必先利其器,第一... 哈哈哈),也是不行,最最最最最后,都快崩溃的时候,把路径名里的 横杠改成下划线,然后完美~~ 应该是类似于路径不支持中文吧,终于解决,看到tomcat默认网页的时候,还是很开心的
  • 最近linux服务器中的Tomcat服务经常挂掉,通过增大句柄数,缓解问题,但是没有根本解决问题,通过命令查看进程的句柄情况。 ![图片说明](https://img-ask.csdn.net/upload/201910/18/1571398601_741878.jpg) ...
  • 问题背景Tomcat经常崩溃crash,想看看JVM内存使用情况,就想到用Jconsole监控,以前只是监控本地的JVM,这次要监控远程的,遇到不少问题。经过几个小时的努力,参考众多网友的资料之后,才最终解决了这个问题...
  • 先来说下当并发达到1000时的场景,我们知道,对于一个tomcat来讲,理论上能处理500的并发量,但实际它能应付的并发量有个三四百就不错,那么当有1000人同时访问系统时,怎么解决问题呢?答案是加tomcat数量(搭建...
  • 1.2.0 请解释下为什么鹿晗发布恋情的时候,微博系统会崩溃,如何解决? 1.2.1 现有一批邮件需要发送给订阅顾客,且有一个集群(集群的节点数不定,会动态扩容缩容)来负责具体的邮件发送任务,如何让系统尽快地...
  • 怎么避免用来削峰的mq中的消息过长,导致mq崩溃 方案: 在进入削峰队列之前,需要判断mq中的消息数目是否过多,如果超过设定的数量限制,直接返回给客户端"已售罄" channel.messageCount("seckill") 可以获取到队列...
  • java 面试题 总结

    2009-09-16 08:45:34
    多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好的解决了应用程序函数同名问题。 2、String是最基本的数据类型吗? 基本数据类型包括byte、int、char、long、float、double、boolean和short。 java.lang....

空空如也

空空如也

1 2
收藏数 23
精华内容 9
关键字:

tomcat崩溃了怎么解决