分类目录归档:软件

软件主菜单:软件技术相关文章。

java内存分析-借助开源工具高效完成Java应用的运行分析

转自: http://www.infoq.com/cn/articles/java-profiling-with-open-source

不止一次,我们都萌发过想对运行中程序的底层状况一探究竟的念头。产生这种需求的原因可能是运行缓慢的服务、Java虚拟机(JVM)崩溃、挂起、死锁、频繁的JVM暂停、突然或持续的高CPU使用率、甚至于可怕的内存溢出(OOME)。好消息是现在已有许多工具能帮你得到Java虚拟机运行过程中的不同参数,这些信息有助于你了解其内部状况,从而诊断上述的各种情况。

在这篇文章中,我将介绍一些优秀的开源工具。其中一些是JVM自带的,另一些则是第三方工具。我将从最简单的工具开始介绍,逐渐过渡到一些比较复杂的工具。本文的目的是帮助你找到合适的调试诊断工具,这样当程序出现执行异常、缓慢或根本不能执行时,手头随时有可用的工具。

好了,让我们出发。

如果程序出现不正常的高内存负载、频繁无响应或内存溢出,通常最好的分析切入点是查看内存对象。幸好JVM内置了工具“jmap”,让它天生就能完成这种任务。

Jmap(借助JPM的一点帮助)

Oracle将jmap描述为一种“输出进程、核心文件、远程调试服务器的共享对象内存映射和堆内存细节”的程序。本文将使用jmap打印一张内存统计图。

为了运行jmap,你需要知道被调试程序的PID(进程标识符)。得到PID的简单办法是使用JVM提供的jps,它能列出机器上每一个JVM进程及其PID。jps输出结果如下图:

图1:jps命令的终端输出

为了打印内存统计图,我们需要打开jmap控制台程序,并输入程序的PID和“-histo:live”选项。如果不添加这个选项,jmap将完整导出该程序的堆内存,这不是我们想要的结果。所以,如果想得到上图中“eureka.Proxy”程序的内存统计图,我们应该用如下命令来运行jmap:

jmap –histo:live 45417

上述命令输出如下:

(点击图片可以放大)

图2:命令jmap -histo:live的输出结果显示了堆中现有对象的个数

结果中每行显示了当前堆中每种类类型的信息,包含被分配的实例个数及其消耗的字节数。

本例中,我请同事有意给程序增加了一处明显的内存泄露。请特别注意位于第8行的类,CelleData。将它与下图显示的4分钟后截屏进行比较:

(点击图片可以放大)

图3:jmap的输出表明CelleData类的对象数目增加了

请注意CelleData类现在已经变为系统中第二多的类,短短4分钟内已经增加了631,701个额外实例。等待约一小时后,我们观察到如下结果:

(点击图片可以放大)

图4:程序执行1小时后jmap的输出结果,显示超过2千5百万个CelleData类实例

现在有超过2千5百万个CelleData类实例,占用了超过1GB内存!我们可以确认这是一个内存泄露。

这类数据信息的好处是,不仅非常有用而且对于很大的JVM堆也能快速反馈结果。我曾经试过检测一个运行频繁并且占用17GB堆内存的程序,使用jmap能够在1分钟内生成程序的性能统计图。

需要注意的是,jmap不是运行分析工具,在生成统计图时JVM可能会暂停,因此当生成统计图时需要确认这种暂停对程序是可接受的。以我的经验,通常在调试一个严重bug时需要生成这种统计图,这种情况下,这些1分钟的暂停对程序来说是可接受的。这里,我们引出了下一个话题 – 半自动的运行分析工具VisualVM。

VisualVM

另一个包含于JVM中的工具是VisualVM,它的开发者将它描述为“一种集成了多个JDK命令行工具的可视化工具,它能为您提供轻量级的运行分析能力”。这样看来,VisualVM是另一种你最有可能用到的事后分析工具,一般是错误已出现或性能问题已经用传统方法(客户抱怨大多属于此类)发现。

继续之前的示例程序和它严重的内存泄露问题,在程序执行30分钟后,VisualVM帮我们绘制了如下图表:

图5:程序初始运行的VisualVM 内存图

从这个图表,我们可以清晰地看到截止到7:00pm,运行仅仅10分钟后,程序已经消耗掉超过1GB的堆空间。又过了23分钟,JVM已经到了它启动参数–Xmx3g最大值,导致程序响应缓慢,系统响应缓慢(持续的垃圾回收)和数量惊人的内存溢出错误。

借助jmap,我们定位了这种内存消耗攀升的原因。修复后,我们让程序重新运行于VisualVM的严格监测之下,观察到下面的情况:

图6:修复内存泄露问题后的VisualVM内存图

如你所见,程序的内存曲线(启动参数仍然为–Xmx3g)有了明显改善。

除了内存图像工具,VisualVM还提供了一个采样器和一个轻量级的剖析器(Profiler)。

VisualVM采样器能周期采样程序CPU和内存的使用情况。得到的统计数据类似jmap的反馈,此外,你还可以通过采样得到方法调用对CPU的占用情况。它让你能快速了解周期采样过程中的方法执行次数:

(点击图片可以放大)

图7:VisualVM方法执行时间表

VisualVM剖析器无需对程序周期采样就可以提供类似采样器的反馈信息,它还可以收集程序在整个正常执行过程中的统计数据(通过操纵程序源代码的字节码)。从剖析器得到的这种统计数据比从采样器而来的更精确和实时。

(点击图片可以放大)

图8:VisualVM剖析器的输出

但是,你必须考虑的另一方面是该剖析器属于一种“暴力”分析工具。它的检测方法本质上是重新定义程序执行中的大多数类和方法,结果必然会明显减缓程序执行速度。例如,上述程序运行部分的常规分析,大约要35秒。开启VisualVM的内存剖析器后,导致程序完成相同分析要31分钟。

我们需要清楚的是VisualVM并非功能齐全的剖析器。它无法在你的产品JVM上持续运行,不会保存分析数据,无法指定阈值,也不会在超过阈值时发出警报。要想更多的了解功能齐全的剖析器的目标。下面,让我们看看BTrace,这个功能齐全的开源java代理程序。

BTrace

想象一下,如果能收集JVM当前的任何信息,那么你感兴趣的信息有哪些?我猜想问题列表会将因人而异,因情形而异。就个人来说,我通常感兴趣的是以下的问题:

  • 程序对堆、非堆、永久保存区(Permanent Generation),以及JVM包含的不同内存池(新生对象区、长期对象区、存活空间等)的内存使用情况
  • 当前程序的线程数量,以及哪种类型线程正在被使用(单独计数)
  • JVM的CUP负载
  • 系统平均负载/系统CPU使用总和
  • 对程序中的某些类和方法,我需要了解它们被调用次数,各自平均执行时间和整体平均时间
  • 对SQL调用的调用计数及执行次数
  • 对硬盘和网络操作的调用计数及执行次数

利用BTrace可以采集到所有以上信息,你可以使用BTrace脚本定义需要采集的数据。方便的是,BTrace脚本就是普通Java类,包含一些特殊注解来定义BTrace在什么地方及如何跟踪你的程序。BTrace脚本会被BTrace编译器-btracec编译成标准的.class文件。

BTrace脚本包含许多部分,正如下图所示。如果需要了解下图脚本的详细内容,请点击该链接或访问BTrace项目网站

由于BTrace仅仅是一个代理,记录结果后,它的任务就算完成了。除了文本输出,BTrace并不具备动态展现被收集信息的功能。缺省情况下,BTrace脚本输出结果将在btrace.class文件所在位置生成一个名为BTrace脚本名.class.btrace的text文件。

我们可以通过给BTrace设置一个额外参数,让它按某时间间隔循环记录日志。切记,它最多能在100个日志文件间循环,当达到*.class.btrace.99,它将覆盖*.class.btrace.00文件。若让循环间隔在一个合理数字(如,每7.5秒)内,你就有充足时间来处理这些输出。只要在java代理的输入参数中加上fileRollMilliseconds=7500,就可以实现日志循环。

BTrace一大缺点是它比较原始,难以定义它的输出格式。你也许非常希望有一种更好的方式来处理BTrace的输出和数据,比如可以用一种一致的图形用户界面来展示。你可能还需要比较不同时间点的数据和超出阈值能发送警告。一个新的开源工具EurekaJ,就此应运而生。

(点击图片可以放大)

图9:激活方法分析时必需的BTrace脚本

EurekaJ

我最初开发EurekaJ是在2008年。那时,我正在寻找一种具有我需要功能的开源剖析器,但没有找到。于是,我开始开发自己的工具。开发过程中,我涉猎了大量不同的技术并参考了许多架构模型,直到EurekaJ第一个版本发布。你可以从项目网站上了解更多的EurekaJ历史,查看源代码或下载并试着安装自己的版本。

EurekaJ提供了两个主要应用:

  1. 一个基于java的管理器程序,可以接收传入的统计数据并一致地以可视化视图展现出来
  2. 一个解析BTrace输出的代理程序,将其转化为JSON格式并输入到EurekaJ管理程序的REST接口

EurekaJ接受两种类型的输入数据格式。EurekaJ代理期望BTrace脚本的输出被格式化为逗号分隔的文件(这点在BTrace中可很容易做到),而EurekaJ管理程序期望它的输入符合它的JSON REST接口格式。这意味着你能通过代理程序或直接经由REST接口来传递度量数据。

借助EurekaJ管理程序,我们可以在一张图上分组显示多个统计数据、可以定义阈值和给接收者发出警报。我们还可以方便的查看收集到的实时数据或历史数据。

所有收集到的数据排序成一种逻辑树结构,其结构由BTrace脚本作者指定。我建议BTrace脚本的作者对相关统计数据分组,这样,当它们显示在EurekaJ中时会更容易理解和观察。例如,我个人喜欢对统计数据进行如下的逻辑分组:

图10:EurekaJ演示程序的统计分组示例

图例

一种需要采集的重要信息是程序运行时的平均系统负载。要是你正面对一个运行缓慢的程序,那么缺陷可能并不在程序自身,而是隐藏到应用驻留的主机某处。我曾经在调试运行缓慢的应用时偶尔发现,真正的根源是病毒扫描程序。如果不进行测量分析,这种事情会很难被发现。考虑到这一点,我们需要能够在一张图中显示系统平均负载和进程加载后产生的负载。下图显示了一个运行EurekaJ 演示程序的Amazon EC2虚拟服务器的2小时平均负载(该应用登录的用户名和密码都是‘user’)。

(点击图片可以放大)

图11:显示平均系统负载的EurekaJ图表

图中,黄色和红色的线条表示警戒阈值。一旦图形超过黄线的次数超过预设的最小警戒次数时,则测量结果到达“警告”状态。类似,若突破红线,测量结果就到达“危险”或“错误”状态。每当发生状态转换,EurekaJ都会发送一封邮件给之前注册的收件人。

在上面的情形中,好像有周期性的事件每20分钟发生一次,从平均负载图上显示的波峰可以看到这一点。首先你要确定的是这个波峰确实由你的程序产生,而非其他原因。我们也可以通过测量进程的CPU负载来确认这点。在EurekaJ树菜单中,选择两个测量点后,两个图表结果会一起快速成像显示出来,其中一个位于另一个下面。

(点击图片可以放大)

图12:同时显示多个图表

在上面的例子中,我们清楚地看到进程CUP占用和系统负载存在必然的联系。

许多应用需要在程序无响应或不可用时及时发出警告。下图是一个Confluence(Atlassian的企业级Wiki软件)的例子。这个例子中,程序内存占用快速上升,直到产生程序内存溢出。这时,Confluence无法处理接收到的请求,同时日志文件记录了各种奇怪的错误。

你可能希望当程序运行导致内存溢出时,程序能立刻抛出一个OOME(内存溢出错误),然而,事实上JVM不会抛出OOME直到它发觉垃圾回收过于缓慢。结果,程序没有完全崩溃,又过了2小时,Java仍然没有抛出OutOfMemoryError,甚至两小时后程序依然在“运行”(意味着JVM进程仍然在运行)。显然,这时任何进程监测工具都不能发现程序已经“停止”。

(点击图片可以放大)

图13:EurekaJ堆图显示内存溢出错误的一种可能情形

注意最后几个小时的执行情况,图表揭示了下面的度量指标。

(点击图片可以放大)

图14:前面图表放大后的效果

EurekaJ使我们可以设置程序的堆内存警告,个人建议最好如此。若程序持续占用堆内存超过95%-98%(取决于堆的大小),几乎可以肯定,程序存在内存问题,要么用–Xmx参数为程序分配更多的堆,要么优化程序使其使用更少内存。同时,EurekaJ未来版本计划增加统计数据不足的警报。

最后的图表示例展示了一个包含4个不同程序内存使用的图表组。这种类型的图表组可方便用来比较一个程序不同部分的、或甚至不同程序之间、服务器之间的数据。下图的这4个程序有不同的内存需求和内存占用模式。

如下图示,不同程序有不同的内存曲线。这些曲线非常依赖一些实际情况,比如使用的架构、缓存数量、用户数、程序负载等。我希望通过下图说明你需要掌握程序在正常和高负载下执行情况的重要性,因为这将直接关系到如何定义报警阈值。

(点击图片可以放大)

图15:EurekaJ图组会将图像彼此叠加在一起

注意性能干扰 – 让非热点区不受影响!

你使用的每一种测量方法似乎都会引起系统性能干扰。一些数据的测量可以被认为“无干扰”(或“忽略不计”),然而另外一些数据的测量可称得上代价昂贵。非常重要的一点是,要知道你需要BTrace测量什么。因此,你要将这种分析工具对程序的性能干扰减少到最小。关于这点,请参考下面的3条原则:

  • 基于“采样”的度量通常可被认为“无影响”。采样CPU负载、进程CPU负载、内存使用和每5-10秒的线程计数,其带来的额外一两个毫秒的影响可被忽略。在我看来,你应该经常收集这类统计数据,它们对你来说不会有什么损耗。
  • 对长时间运行的任务的测量也可被认为“无影响”。通常,它仅会对每个被测量方法带来1700-2500纳秒的影响。如果你正测量这些对象的执行时间:SQL查询、网络流量、硬盘读写或一个预期范围在40毫秒(磁盘存取)到1秒(Servlet处理)之间的Servlet处理过程,那么对这些对象每个增加额外的2500纳秒左右的时间也是可接受的。
  • 绝对不要对循环内执行的方法进行测量

寻找程序热点区的一个通用规则是不要影响非热点区域。例如,考虑下面的类:

(点击图片可以放大)

图16:需要测量的简单的数据访问接口类

第5行的SQL执行时间可能使readStatFromDatabase方法可能成为一个热点。当查询返回相当多的数据行时,它无疑会成为一个热点,这对13行(程序和数据库服务器之间的网络流量)和14-16行(结果集中每行所需处理)会造成负面影响。如果从数据库返回结果时间过长,该方法也会成为一个热点(在13行)。

方法buildNewStat就其本身来说似乎绝不会成为一个热点。即使被多次执行,每次调用都会在几纳秒内完成。另一方面,若给每次调用增加了2500纳秒的测量采集干扰,则无论SQL何时被执行,都势必会让该方法看起来像个热点。因此,我们要避免测量它。

(点击图片可以放大)

图17:显示上面类哪些部分可以被测量,哪些需要避免

建立完整的运行分析

使用EurekaJ建立一个完整的运行分析,需要以下几个主要部分:

  • 准备需要监测/操纵的程序
  • BTrace – java代理
  • 告知BTrace如何测量的BTrace脚本
  • 存储BTrace输出的文件系统
  • 将BTrace输出传输到EurekaJ管理器的EurekaJ代理
  • 安装好的EurekaJ管理器(本地安装或可通过互联网访问的远程安装)

(点击图片可以放大)

图18:使用本文所描述工具对程序进行运行分析的概览图

关于这些产品的完整安装手册,请访问EurekaJ项目网站

总结

这篇文章给我们介绍了一些用于程序运行分析的开源工具,它们不仅能帮我们完成对运行中JVM的深度分析,而且可以帮助我们对开发、测试和程序部署进行多方位的持续监测。

希望你已经开始了解不断收集度量信息的好处和超过阈值后及时报警能力的重要性。

非常感谢!

参考

[1] jmap 文档

[2] BTrace 脚本概念

[3] BTrace 项目网站

[4] EurekaJ 文档

[5] EurekaJ 项目网站

关于作者

Joachim Haagen Skeie,挪威奥斯陆的Kantega AS公司的Java和Web技术的资深顾问,关注程序性能分析和开源软件。你可以通过他的Twitter账号来联系。

 

 

查看英文原文:Effective Java Profiling With Open Source Tools


感谢胡键对本文的审校。

java 内存分析-JAVA进程内存用量高的分析与解决

转自:http://chou.it/2013/09/why-java-consume-a-lot-of-rss-memory

首先看一下一个java进程的jmap输出:

[lex@chou ~]$ jmap -heap 837
Attaching to process ID 837, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 20.10-b01

using thread-local object allocation.
Parallel GC with 2 thread(s)

Heap Configuration:
   MinHeapFreeRatio = 40
   MaxHeapFreeRatio = 70
   MaxHeapSize      = 4294967296 (4096.0MB)
   NewSize          = 1310720 (1.25MB)
   MaxNewSize       = 17592186044415 MB
   OldSize          = 5439488 (5.1875MB)
   NewRatio         = 2
   SurvivorRatio    = 8
   PermSize         = 21757952 (20.75MB)
   MaxPermSize      = 85983232 (82.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 41025536 (39.125MB)
   used     = 18413552 (17.560531616210938MB)
   free     = 22611984 (21.564468383789062MB)
   44.883147900858624% used
From Space:
   capacity = 4325376 (4.125MB)
   used     = 3702784 (3.53125MB)
   free     = 622592 (0.59375MB)
   85.60606060606061% used
To Space:
   capacity = 4521984 (4.3125MB)
   used     = 0 (0.0MB)
   free     = 4521984 (4.3125MB)
   0.0% used
PS Old Generation
   capacity = 539820032 (514.8125MB)
   used     = 108786168 (103.74657440185547MB)
   free     = 431033864 (411.06592559814453MB)
   20.152302906758376% used
PS Perm Generation
   capacity = 85983232 (82.0MB)
   used     = 60770232 (57.95500946044922MB)
   free     = 25213000 (24.04499053955078MB)
   70.67684080542588% used

然后再用ps看看:

[lex@chou ~]$ ps -p 837 -o vsz,rss
   VSZ   RSS
7794992 3047320

关于这里的几个generation网上资料一大把就不细说了,这里算一下求和可以得知前者总共给Java环境分配了644M的内存,而ps输出的VSZ和RSS分别是7.4G和2.9G,这到底是怎么回事呢?
前面jmap输出的内容里,MaxHeapSize 是在命令行上配的,-Xmx4096m,这个java程序可以用到的最大堆内存。
VSZ是指已分配的线性空间大小,这个大小通常并不等于程序实际用到的内存大小,产生这个的可能性很多,比如内存映射,共享的动态库,或者向系统申请了更多的堆,都会扩展线性空间大小,要查看一个进程有哪些内存映射,可以使用 pmap 命令来查看:

[lex@chou ~]$ pmap -x 837
837:   java
Address           Kbytes     RSS   Dirty Mode   Mapping
0000000040000000      36       4       0 r-x--  java
0000000040108000       8       8       8 rwx--  java
00000000418c9000   13676   13676   13676 rwx--    [ anon ]
00000006fae00000   83968   83968   83968 rwx--    [ anon ]
0000000700000000  527168  451636  451636 rwx--    [ anon ]
00000007202d0000  127040       0       0 -----    [ anon ]
...
...
00007f55ee124000       4       4       0 r-xs-  az.png
00007fff017ff000       4       4       0 r-x--    [ anon ]
ffffffffff600000       4       0       0 r-x--    [ anon ]
----------------  ------  ------  ------
total kB         7796020 3037264 3023928

这里可以看到很多anon,这些表示这块内存是由mmap分配的。

RSZ是Resident Set Size,常驻内存大小,即进程实际占用的物理内存大小, 在现在这个例子当中,RSZ和实际堆内存占用差了2.3G,这2.3G的内存组成分别为:

JVM本身需要的内存,包括其加载的第三方库以及这些库分配的内存
NIO的DirectBuffer是分配的native memory
内存映射文件,包括JVM加载的一些JAR和第三方库,以及程序内部用到的。上面 pmap 输出的内容里,有一些静态文件所占用的大小不在Java的heap里,因此作为一个Web服务器,赶紧把静态文件从这个Web服务器中人移开吧,放到nginx或者CDN里去吧。
JIT, JVM会将Class编译成native代码,这些内存也不会少,如果使用了Spring的AOP,CGLIB会生成更多的类,JIT的内存开销也会随之变大,而且Class本身JVM的GC会将其放到Perm Generation里去,很难被回收掉,面对这种情况,应该让JVM使用ConcurrentMarkSweep GC,并启用这个GC的相关参数允许将不使用的class从Perm Generation中移除, 参数配置: -XX:+UseConcMarkSweepGC -X:+CMSPermGenSweepingEnabled -X:+CMSClassUnloadingEnabled,如果不需要移除而Perm Generation空间不够,可以加大一点: -X:PermSize=256M -X:MaxPermSize=512M
JNI,一些JNI接口调用的native库也会分配一些内存,如果遇到JNI库的内存泄露,可以使用valgrind等内存泄露工具来检测
线程栈,每个线程都会有自己的栈空间,如果线程一多,这个的开销就很明显了
jmap/jstack 采样,频繁的采样也会增加内存占用,如果你有服务器健康监控,记得这个频率别太高,否则健康监控变成致病监控了。
关于JVM的几个GC堆和GC的情况,可以用jstat来监控,例如监控进程837每隔1000毫秒刷新一次,输出20次:

[lex@chou ~]$ jstat -gcutil 837 1000 20
  S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT   
  0.00  80.43  24.62  87.44  98.29   7101  119.652    40   19.719  139.371
  0.00  80.43  33.14  87.44  98.29   7101  119.652    40   19.719  139.371

几个字段分别含义如下:

S0 年轻代中第一个survivor(幸存区)已使用的占当前容量百分比
S1 年轻代中第二个survivor(幸存区)已使用的占当前容量百分比
E 年轻代中Eden(伊甸园)已使用的占当前容量百分比
O old代已使用的占当前容量百分比
P perm代已使用的占当前容量百分比
YGC 从应用程序启动到采样时年轻代中gc次数
YGCT 从应用程序启动到采样时年轻代中gc所用时间(s)
FGC 从应用程序启动到采样时old代(全gc)gc次数
FGCT 从应用程序启动到采样时old代(全gc)gc所用时间(s)
GCT 从应用程序启动到采样时gc用的总时间(s)
结论
因此如果正常情况下jmap输出的内存占用远小于 RSZ,可以不用太担心,除非发生一些严重错误,比如PermGen空间满了导致OutOfMemoryError发生,或者RSZ太高导致引起系统公愤被OOM Killer给干掉,就得注意了,该加内存加内存,没钱买内存加交换空间,或者按上面列的组成部分逐一排除。
这几个内存指标之间的关系是:VSZ >> RSZ >> Java程序实际使用的堆大小

相关的参考资料:

http://www.ibm.com/developerworks/java/library/j-nativememory-linux/

http://www.oracle.com/technetwork/java/javase/index-137495.html

linux下使用私有驱动时需谨慎升级kernel 和 x server

我的系统是 linux mint 13 xfce 版本, 显卡为ATI 的。 曾经为了追求最大限度发挥显卡性能而安装了ATI的私有驱动。

今天,我在配置一个软件过程中,执行了

     sudo apt-get upgrade
   

命令, 命令提醒我可以安装新版本的linux kernel 和 x server了, 我一看,好啊!新版本应该好很多! 然后立马输入了 “Y”, 让系统升级。
升级完成后,我继续使用电脑做别的事情,一切看起来非常的正常、平静。没有任何要发生异常的预兆。
晚上,我想电脑累了一天,重启下吧! 这下意外发生了,“我的系统启动后没有任何提示!黑屏啊!” 我想会不会因为升级导致grub不能引导?还是kernel 有问题了? 我强制关机,继续重启,有时居然可以进入linux mint 的启动界面, 但会卡在这里不动,而且奇怪的是,这个页面和以前不一样,字体明显很
粗糙。

此时我分析可能出错的地方有:
1. grub 引导程序损坏
2. 新kernel 有bug
3. x server 有问题

继续分析:如果grub引导坏掉,不可能有时可以进入linux mint 启动界面,所以可能性不大。kernel 如果有问题,也不可能进入linux mint 启动界面,更不应该在我 按 ctrl + alt + f1 时可以进入 非图形界面的。所以,出问题可能性最大的应该就是x server 了。

接下来,重启电脑,启动时注意按 SHIFT键, 在grub 引导菜单里面选择 recovery model 的菜单, 进入后,选择 “resume Resume normal boot ”, 按tab键将光标移动到 OK 按钮,回车,系统提示说“x server 不能启动,配置有问题。” 这样,我们就可以确认x server 不能正常启动了。

那我们就看看

     nano /etc/X11/xorg.conf
    

发现我的配置是ati的驱动,有下面内容,

Section "Monitor"
	Identifier   "aticonfig-Monitor[0]-0"
	Option	    "VendorName" "ATI Proprietary Driver"
	Option	    "ModelName" "Generic Autodetecting Monitor"
	Option	    "DPMS" "true"
EndSection

Section "Device"
	Identifier  "aticonfig-Device[0]-0"
	Driver      "fglrx"
	BusID       "PCI:1:0:0"
EndSection
    

猜测是系统x server , kernel 升级后私有驱动不兼容导致,那恢复成开源驱动。恢复方法为:卸载私有驱动。

恢复步骤:
1. 重启系统,正常启动过程中(不是recovery model), 按 ctrl + alt + f1 进入命令行。
2. 登录后, 切换为root用户

          sudo -i
        

3. 卸载ati 私有驱动

sudo sh /usr/share/ati/fglrx-uninstall.sh
sudo apt-get remove --purge fglrx fglrx_* fglrx-amdcccle* fglrx-dev*
        

4. 重启系统

           sudo reboot
         

这次启动过程好像比较慢,但是,终于看到熟悉的 linux mint 登录界面了。说明系统恢复了!

这次,我差一点就行重装系统,重装很麻烦,最终曲折的恢复了系统。

不过,从这件事情上面得到歌总结:
如果安装了私有驱动 , 升级系统的 linux kernel 和 x server时要特别小心。最好不要升级这两个项目。

出发有特别需要,电脑上面的系统级软件还是不升级为妙。稳定才是我们的根本需求。