精华内容
下载资源
问答
  • 2020-02-16 13:02:46

    问题一:遭遇403 forbidden问题

    打开主页报错403 forbidden,通过查看日志文件/usr/local/nginx/logs/error.log发现权限问题,下图日志文件,

    2020/02/16 12:20:55 [error] 3977#0: *37 "/home/www/webroot/php/wordpress/wp-admin/index.html" is 
    forbidden (13: Permission denied), client: 192.168.0.5, server: localhost, request: "GET / 
    HTTP/1.1", host: "192.168.0.11"
    

    13 :Permission denied即权限问题,修改nginx.conf文件user=用户名,该用户名需与你运行nginx的用户名一致,如用root运行nginx那么就需要在nginx.conf文件中的user设置为user=root即可。还有一种是网站目录权限问题,本人没有遇到,不太清楚。

    问题二:找不到该文件,file not found本来要报404的结果我把root改了,提示找不到文件,还是看日志文件

    2020/02/16 12:30:10 [error] 4629#0: *44 open() "/home/www/webroot/php/wordpress/wp-admin/404.html" 
    failed (2: No such file or directory), client: 192.168.0.5, server: localhost, request: "GET /wp-
    includes/js/wp-emoji-release.min.js?ver=5.3.2 HTTP/1.1", host: "192.168.0.11", referrer: 
    "http://192.168.0.11/"
    

    本来要报404的但由于找不到该文件 *44 open() "/home/www/webroot/php/wordpress/wp-admin/404.html"failed (2: No such file or directory),通过报错日志发现目录设置有问题,就需要在nginx.conf配置文件中修改root值即可。

    总结:日志文件是学习的重重之重,切记,切记,学会分析日志文件,不要盲目上网查找答案,通过自己分析日志找到答案印象更深刻,也学到了知识。

    更多相关内容
  • 学会发现问题--提出问题--分析问题--解决问题

    万次阅读 热门讨论 2018-01-23 18:01:07
     今天下午做了一次英语快照,第一次做总会出现很多问题 问题:  1、摄像问题。时间过去一半了才想起来没有摄像机,于是我们又在摄像机上面浪费了5分钟左右的时间,更可惜的是大家都在等待着,所以因为我...

    罗塞塔活动


      今天下午做了一次英语快照,第一次做总会出现很多问题


    问题:


     1、摄像问题。时间过去一半了才想起来没有摄像机,于是我们又在摄像机上面浪费了5分钟左右的时间,更可惜的是大家都在等待着,所以因为我的疏忽,白白浪费了Bill和大家的时间。

     2、电脑声音问题。做游戏的时候大家听不到声音,有的单词声音很小,有的正好,导致做游戏好多人听不见影响大家的积极性。两点思考问题不全面,或者说不用心,没有特别注意这些细节,下回一定要思考全面,认真对待。


      这次罗塞塔的快照感谢Bill师哥的支持,感谢亲爱的小伙伴们积极参与,也给我一次锻炼的机会!!!



    终身成长


      快照过后,Bill师哥与我们围坐在一起交流。这里就谈到了关于“如何提问”的主题。


      大家不管与团队一起学习也好,还是个人学习也好,发现了问题为什么不及时说出来?我们围绕这个问题讨论了很久,下面是在听每位同学所说的为什么会有这样的情绪所做得总结


    1、我跟着团队走就好了,怎么组织我就怎么跟着学

    2、在团队里保持一样的方法行进,我突然变得突兀是不是很不好,让人反感

    3、还有以学习者的角度和组织者的角度

     (1)学习者:跟上面2点类似,或者当时想起来了没有说,过了一会忘了,等到回到自己座位上又想起来了导致这个问题一直没有机会说出来

     (2)组织者:没有做到全方面把控,没有考虑到船员们是否可以接受这种学习方法。一些方法只是自己知道了,自己安排如何学习,没有开会跟船员们说以后要去怎么做,怎么去学,导致发现不了问题!


    解决方法:

      .每次团队组织完学习之后留出适当的时间,在这时间里大家畅所欲言,把自己想到的问题提出来,大家一起解决!

      .船长积极与组员沟通


       不管怎样,我们大家要始终保持成一个团队,我们既要学会借助团队,比如:个人学习遇到了问题记录下来拿到团队中去解决;团队的学习料率比个人要高;学习如何与人相处,如何做一个合格的领导者等等。还要不依赖团队:我们在学习中不能总靠着团队的力量,什么事不能等别人去叫你,然后按照别人安排好的去做,那样太被动了,自己要制定一个计划,既要跟别人玩到一起,自己的学习也要游刃有余!


       Bill师哥说过每个人要学会总结,不会总结的人,一辈子都会在原地踏步。领导从你这得不到好的建议,自己遇到问题解决之后不总结下一次依然不会,为什么别人经常能发现问题,而你不能的原因就是缺少总结,不敢去挑战!


    能力是锻炼出来的,不是固定的!!!


    哪里有抱怨,哪里就有机会!!!



    展开全文
  • CMS 常见问题分析解决方案

    千次阅读 2021-01-25 00:34:17
    1.0引言 自 Sun 发布 Java 语言以来,开始使用 GC 技术来进行内存自动管理,避免了手动管理带来的悬挂指针(Dangling Pointer)问题,很大程度上提升了开发效率,从此 ...也就是说 GC 要解决的根本问题这么多年一直都.

    1.0 引言

    自 Sun 发布 Java 语言以来,开始使用 GC 技术来进行内存自动管理,避免了手动管理带来的悬挂指针(Dangling Pointer)问题,很大程度上提升了开发效率,从此 GC 技术也一举成名。GC 有着非常悠久的历史,1960 年有着“Lisp 之父”和“人工智能之父”之称的 John McCarthy 就在论文中发布了 GC 算法,60 年以来, GC 技术的发展也突飞猛进,但不管是多么前沿的收集器也都是基于三种基本算法的组合或应用,也就是说 GC 要解决的根本问题这么多年一直都没有变过。笔者认为,在不太远的将来, GC 技术依然不会过时,比起日新月异的新技术,GC 这门古典技术更值得我们学习。

    目前,互联网上 Java 的 GC 资料要么是主要讲解理论,要么就是针对单一场景的 GC 问题进行了剖析,对整个体系总结的资料少之又少。

    GC 问题处理能力能不能系统性掌握?一些影响因素都是互为因果的问题该怎么分析?比如一个服务 RT 突然上涨,有 GC 耗时增大、线程 Block 增多、慢查询增多、CPU 负载高四个表象,到底哪个是诱因?如何判断 GC 有没有问题?使用 CMS 有哪些常见问题?如何判断根因是什么?如何解决或避免这些问题?阅读完本文,相信你将会对 CMS GC 的问题处理有一个系统性的认知,更能游刃有余地解决这些问题,下面就让我们开始吧!

     

    1.1 概览

    想要系统性地掌握 GC 问题处理,笔者这里给出一个学习路径,整体文章的框架也是按照这个结构展开,主要分四大步。

    • 建立知识体系: 从 JVM 的内存结构到垃圾收集的算法和收集器,学习 GC 的基础知识,掌握一些常用的 GC 问题分析工具。

    • 确定评价指标: 了解基本 GC 的评价方法,摸清如何设定独立系统的指标,以及在业务场景中判断 GC 是否存在问题的手段。

    • 场景调优实践: 运用掌握的知识和系统评价指标,分析与解决九种 CMS 中常见 GC 问题场景。

    • 总结优化经验: 对整体过程做总结并提出笔者的几点建议,同时将总结到的经验完善到知识体系之中。

     

    2.1 基础概念

    • GC: GC 本身有三种语义,下文需要根据具体场景带入不同的语义:

      • Garbage Collection:垃圾收集技术,名词。

      • Garbage Collector:垃圾收集器,名词。

      • Garbage Collecting:垃圾收集动作,动词。

    • Mutator: 生产垃圾的角色,也就是我们的应用程序,垃圾制造者,通过 Allocator 进行 allocate 和 free。

    • TLAB: Thread Local Allocation Buffer 的简写,基于 CAS 的独享线程(Mutator Threads)可以优先将对象分配在 Eden 中的一块内存,因为是 Java 线程独享的内存区没有锁竞争,所以分配速度更快,每个 TLAB 都是一个线程独享的。

    • Card Table: 中文翻译为卡表,主要是用来标记卡页的状态,每个卡表项对应一个卡页。当卡页中一个对象引用有写操作时,写屏障将会标记对象所在的卡表状态改为 dirty,卡表的本质是用来解决跨代引用的问题。具体怎么解决的可以参考 StackOverflow 上的这个问题how-actually-card-table-and-writer-barrier-works,或者研读一下 cardTableRS.app 中的源码。

    2.2 JVM 内存划分

    从 JCP(Java Community Process)的官网中可以看到,目前 Java 版本最新已经到了 Java 16,未来的 Java 17 以及现在的 Java 11 和 Java 8 是 LTS 版本,JVM 规范也在随着迭代在变更,由于本文主要讨论 CMS,此处还是放 Java 8 的内存结构。

    GC 主要工作在 Heap 区和 MetaSpace 区(上图蓝色部分),在 Direct Memory 中,如果使用的是 DirectByteBuffer,那么在分配内存不够时则是 GC 通过 Cleaner#clean 间接管理。

    任何自动内存管理系统都会面临的步骤:为新对象分配空间,然后收集垃圾对象空间,下面我们就展开介绍一下这些基础知识。

    2.3 分配对象

    Java 中对象地址操作主要使用 Unsafe 调用了 C 的 allocate 和 free 两个方法,分配方法有两种:

    • 空闲链表(free list): 通过额外的存储记录空闲的地址,将随机 IO 变为顺序 IO,但带来了额外的空间消耗。

    • 碰撞指针(bump pointer): 通过一个指针作为分界点,需要分配内存时,仅需把指针往空闲的一端移动与对象大小相等的距离,分配效率较高,但使用场景有限。

    2.4 收集对象

    2.4.1 识别垃圾

    • 引用计数法(Reference Counting): 对每个对象的引用进行计数,每当有一个地方引用它时计数器 +1、引用失效则 -1,引用的计数放到对象头中,大于 0 的对象被认为是存活对象。虽然循环引用的问题可通过 Recycler 算法解决,但是在多线程环境下,引用计数变更也要进行昂贵的同步操作,性能较低,早期的编程语言会采用此算法。

    • 可达性分析,又称引用链法(Tracing GC): 从 GC Root 开始进行对象搜索,可以被搜索到的对象即为可达对象,此时还不足以判断对象是否存活/死亡,需要经过多次标记才能更加准确地确定,整个连通图之外的对象便可以作为垃圾被回收掉。目前 Java 中主流的虚拟机均采用此算法。

    备注:引用计数法是可以处理循环引用问题的,下次面试时不要再这么说啦~ ~

    2.4.2 收集算法

    自从有自动内存管理出现之时就有的一些收集算法,不同的收集器也是在不同场景下进行组合。

    • Mark-Sweep(标记-清除): 回收过程主要分为两个阶段,第一阶段为追踪(Tracing)阶段,即从 GC Root 开始遍历对象图,并标记(Mark)所遇到的每个对象,第二阶段为清除(Sweep)阶段,即回收器检查堆中每一个对象,并将所有未被标记的对象进行回收,整个过程不会发生对象移动。整个算法在不同的实现中会使用三色抽象(Tricolour Abstraction)、位图标记(BitMap)等技术来提高算法的效率,存活对象较多时较高效。

    • Mark-Compact (标记-整理): 这个算法的主要目的就是解决在非移动式回收器中都会存在的碎片化问题,也分为两个阶段,第一阶段与 Mark-Sweep 类似,第二阶段则会对存活对象按照整理顺序(Compaction Order)进行整理。主要实现有双指针(Two-Finger)回收算法、滑动回收(Lisp2)算法和引线整理(Threaded Compaction)算法等。

    • Copying(复制): 将空间分为两个大小相同的 From 和 To 两个半区,同一时间只会使用其中一个,每次进行回收时将一个半区的存活对象通过复制的方式转移到另一个半区。有递归(Robert R. Fenichel 和 Jerome C. Yochelson提出)和迭代(Cheney 提出)算法,以及解决了前两者递归栈、缓存行等问题的近似优先搜索算法。复制算法可以通过碰撞指针的方式进行快速地分配内存,但是也存在着空间利用率不高的缺点,另外就是存活对象比较大时复制的成本比较高。

    三种算法在是否移动对象、空间和时间方面的一些对比,假设存活对象数量为 *L*、堆空间大小为 *H*,则:

    虽然 compaction 与 copying 都涉及移动对象,但取决于具体算法,compaction 可能要先计算一次对象的目标地址,然后修正指针,最后再移动对象。copying 则可以把这几件事情合为一体来做,所以可以快一些。另外,还需要留意 GC 带来的开销不能只看 Collector 的耗时,还得看 Allocator 。如果能保证内存没碎片,分配就可以用 pointer bumping 方式,只需要挪一个指针就完成了分配,非常快。而如果内存有碎片就得用 freelist 之类的方式管理,分配速度通常会慢一些

    2.5 收集器

    目前在 Hotspot VM 中主要有分代收集和分区收集两大类,具体可以看下面的这个图,不过未来会逐渐向分区收集发展。公司内部有部分业务尝试用了 ZGC(感兴趣的同学可以学习下这篇文章 Java11 ZGC垃圾回收器),其余基本都停留在 CMS 和 G1 上。另外在 JDK11 后提供了一个不执行任何垃圾回收动作的回收器 Epsilon(A No-Op Garbage Collector)用作性能分析。另外一个就是 Azul 的 Zing JVM,其 C4(Concurrent Continuously Compacting Collector)收集器也在业内有一定的影响力。

    2.5.1 分代收集器

    • ParNew: 一款多线程的收集器,采用复制算法,主要工作在 Young 区,可以通过 -XX:ParallelGCThreads 参数来控制收集的线程数,整个过程都是 STW 的,常与 CMS 组合使用。

    • CMS: 以获取最短回收停顿时间为目标,采用“标记-清除”算法,分 4 大步进行垃圾收集,其中初始标记和重新标记会 STW ,多数应用于互联网站或者 B/S 系统的服务器端上,JDK9 被标记弃用,JDK14 被删除,详情可见 JEP 363

    2.5.2 分区收集器

    • G1: 一种服务器端的垃圾收集器,应用在多处理器和大容量内存环境中,在实现高吞吐量的同时,尽可能地满足垃圾收集暂停时间的要求。

    • ZGC: JDK11 中推出的一款低延迟垃圾回收器,适用于大内存低延迟服务的内存管理和回收,SPECjbb 2015 基准测试,在 128G 的大堆下,最大停顿时间才 1.68 ms,停顿时间远胜于 G1 和 CMS。

    • Shenandoah: 由 Red Hat 的一个团队负责开发,与 G1 类似,基于 Region 设计的垃圾收集器,但不需要 Remember Set 或者 Card Table 来记录跨 Region 引用,停顿时间和堆的大小没有任何关系。停顿时间与 ZGC 接近,下图为与 CMS 和 G1 等收集器的 benchmark。

    2.5.3 常用收集器

    目前使用最多的是 CMS 和 G1 收集器,二者都有分代的概念,主要内存结构如下:

    2.6 常用工具

    工欲善其事,必先利其器,此处列出一些笔者常用的工具,具体情况大家可以自由选择,本文的问题都是使用这些工具来定位和分析的。

    2.6.1 命令行终端

    • 标准终端类:jps、jinfo、jstat、jstack、jmap
    • 功能整合类:jcmd、vjtools、arthas、greys

    2.6.2 可视化界面

    • 简易:JConsole、JVisualvm、HA、GCHisto、GCViewer
    • 进阶:MAT、JProfiler

     

    3.1 判断 GC 有没有问题?

    3.1.1 设定评价标准

    评判 GC 的两个核心指标:

    • 延迟(Latency): 也可以理解为最大停顿时间,即垃圾收集过程中一次 STW 的最长时间,越短越好,一定程度上可以接受频次的增大,GC 技术的主要发展方向。

    • 吞吐量(Throughput): 应用系统的生命周期内,由于 GC 线程会占用 Mutator 当前可用的 CPU 时钟周期,吞吐量即为 Mutator 有效花费的时间占系统总运行时间的百分比,例如系统运行了 100 min,GC 耗时 1 min,则系统吞吐量为 99%,吞吐量优先的收集器可以接受较长的停顿。

    简而言之,即为一次停顿的时间不超过应用服务的 TP9999,GC 的吞吐量不小于 99.99%。举个例子,假设某个服务 A 的 TP9999 为 80 ms,平均 GC 停顿为 30 ms,那么该服务的最大停顿时间最好不要超过 80 ms,GC 频次控制在 5 min 以上一次。如果满足不了,那就需要调优或者通过更多资源来进行并联冗余。(大家可以先停下来,看看监控平台上面的 gc.meantime 分钟级别指标,如果超过了 6 ms 那单机 GC 吞吐量就达不到 4 个 9 了。)

    备注:除了这两个指标之外还有 Footprint(资源量大小测量)、反应速度等指标,互联网这种实时系统追求低延迟,而很多嵌入式系统则追求 Footprint。

    3.1.2 读懂 GC Cause

    拿到 GC 日志,我们就可以简单分析 GC 情况了,通过一些工具,我们可以比较直观地看到 Cause 的分布情况,如下图就是使用 gceasy 绘制的图表:

    重点需要关注的几个GC Cause:

    • System.gc(): 手动触发GC操作。

    • CMS: CMS GC 在执行过程中的一些动作,重点关注 CMS Initial Mark 和 CMS Final Remark 两个 STW 阶段。

    • Promotion Failure: Old 区没有足够的空间分配给 Young 区晋升的对象(即使总可用内存足够大)。

    • Concurrent Mode Failure: CMS GC 运行期间,Old 区预留的空间不足以分配给新的对象,此时收集器会发生退化,严重影响 GC 性能,下面的一个案例即为这种场景。

    • GCLocker Initiated GC: 如果线程执行在 JNI 临界区时,刚好需要进行 GC,此时 GC Locker 将会阻止 GC 的发生,同时阻止其他线程进入 JNI 临界区,直到最后一个线程退出临界区时触发一次 GC。

     

    3.2 判断是不是 GC 引发的问题?

    到底是结果(现象)还是原因,在一次 GC 问题处理的过程中,如何判断是 GC 导致的故障,还是系统本身引发 GC 问题。这里继续拿在本文开头提到的一个 Case:“GC 耗时增大、线程 Block 增多、慢查询增多、CPU 负载高等四个表象,如何判断哪个是根因?”,笔者这里根据自己的经验大致整理了四种判断方法供参考:

    • 时序分析: 先发生的事件是根因的概率更大,通过监控手段分析各个指标的异常时间点,还原事件时间线,如先观察到 CPU 负载高(要有足够的时间 Gap),那么整个问题影响链就可能是:CPU 负载高 -> 慢查询增多 -> GC 耗时增大 -> 线程Block增多 -> RT 上涨。

    • 概率分析: 使用统计概率学,结合历史问题的经验进行推断,由近到远按类型分析,如过往慢查的问题比较多,那么整个问题影响链就可能是:慢查询增多 -> GC 耗时增大 -> CPU 负载高 -> 线程 Block 增多 -> RT上涨。

    • 实验分析: 通过故障演练等方式对问题现场进行模拟,触发其中部分条件(一个或多个),观察是否会发生问题,如只触发线程 Block 就会发生问题,那么整个问题影响链就可能是:线程Block增多 -> CPU 负载高 -> 慢查询增多 -> GC 耗时增大 -> RT 上涨。

    • 反证分析: 对其中某一表象进行反证分析,即判断表象的发不发生跟结果是否有相关性,例如我们从整个集群的角度观察到某些节点慢查和 CPU 都正常,但也出了问题,那么整个问题影响链就可能是:GC 耗时增大 -> 线程 Block 增多 -> RT 上涨。

    不同的根因,后续的分析方法是完全不同的。如果是 CPU 负载高那可能需要用火焰图看下热点、如果是慢查询增多那可能需要看下 DB 情况、如果是线程 Block 引起那可能需要看下锁竞争的情况,最后如果各个表象证明都没有问题,那可能 GC 确实存在问题,可以继续分析 GC 问题了。

     

    3.3 GC 问题分类

    笔者选取了九种不同类型的 GC 问题,覆盖了大部分场景,如果有更好的场景,欢迎在评论区给出。

    • Unexpected GC: 意外发生的 GC,实际上不需要发生,我们可以通过一些手段去避免。

      • Space Shock: 空间震荡问题,参见“场景一:动态扩容引起的空间震荡”。
      • Explicit GC: 显示执行 GC 问题,参见“场景二:显式 GC 的去与留”。
    • Partial GC: 部分收集操作的 GC,只对某些分代/分区进行回收。

      • Young GC: 分代收集里面的 Young 区收集动作,也可以叫做 Minor GC。

        • ParNew: Young GC 频繁,参见“场景四:过早晋升”。
      • Old GC: 分代收集里面的 Old 区收集动作,也可以叫做 Major GC,有些也会叫做 Full GC,但其实这种叫法是不规范的,在 CMS 发生 Foreground GC 时才是 Full GC,CMSScavengeBeforeRemark 参数也只是在 Remark 前触发一次Young GC。

        • CMS: Old GC 频繁,参见“场景五:CMS Old GC 频繁”。
        • CMS: Old GC 不频繁但单次耗时大,参见“场景六:单次 CMS Old GC 耗时长”。
    • Full GC: 全量收集的 GC,对整个堆进行回收,STW 时间会比较长,一旦发生,影响较大,也可以叫做 Major GC,参见“场景七:内存碎片&收集器退化”。

    • MetaSpace: 元空间回收引发问题,参见“场景三:MetaSpace 区 OOM”。

    • Direct Memory: 直接内存(也可以称作为堆外内存)回收引发问题,参见“场景八:堆外内存 OOM”。

    • JNI: 本地 Native 方法引发问题,参见“场景九:JNI 引发的 GC 问题”。

    3.3.1 排查难度

    一个问题的解决难度跟它的常见程度成反比,大部分我们都可以通过各种搜索引擎找到类似的问题,然后用同样的手段尝试去解决。当一个问题在各种网站上都找不到相似的问题时,那么可能会有两种情况,一种这不是一个问题,另一种就是遇到一个隐藏比较深的问题,遇到这种问题可能就要深入到源码级别去调试了。以下 GC 问题场景,排查难度从上到下依次递增。

     

    4. 常见场景分析与解决

    4.1 场景一:动态扩容引起的空间震荡

    4.1.1 现象

    服务刚刚启动时 GC 次数较多,最大空间剩余很多但是依然发生 GC,这种情况我们可以通过观察 GC 日志或者通过监控工具来观察堆的空间变化情况即可。GC Cause 一般为 Allocation Failure,且在 GC 日志中会观察到经历一次 GC ,堆内各个空间的大小会被调整,如下图所示

    4.1.2 原因

    在 JVM 的参数中 -Xms 和 -Xmx 设置的不一致,在初始化时只会初始 -Xms 大小的空间存储信息,每当空间不够用时再向操作系统申请,这样的话必然要进行一次 GC。具体是通过 ConcurrentMarkSweepGeneration::compute_new_size() 方法计算新的空间大小:

    void ConcurrentMarkSweepGeneration::compute_new_size() {
      assert_locked_or_safepoint(Heap_lock);
    
      // If incremental collection failed, we just want to expand
      // to the limit.
      if (incremental_collection_failed()) {
        clear_incremental_collection_failed();
        grow_to_reserved();
        return;
      }
    
      // The heap has been compacted but not reset yet.
      // Any metric such as free() or used() will be incorrect.
    
      CardGeneration::compute_new_size();
    
      // Reset again after a possible resizing
      if (did_compact()) {
        cmsSpace()->reset_after_compaction();
      }
    }

    另外,如果空间剩余很多时也会进行缩容操作,JVM 通过 -XX:MinHeapFreeRatio 和 -XX:MaxHeapFreeRatio 来控制扩容和缩容的比例,调节这两个值也可以控制伸缩的时机,例如扩容便是使用 GenCollectedHeap::expand_heap_and_allocate() 来完成的,代码如下:

    HeapWord* GenCollectedHeap::expand_heap_and_allocate(size_t size, bool   is_tlab) {
      HeapWord* result = NULL;
      if (_old_gen->should_allocate(size, is_tlab)) {
        result = _old_gen->expand_and_allocate(size, is_tlab);
      }
      if (result == NULL) {
        if (_young_gen->should_allocate(size, is_tlab)) {
          result = _young_gen->expand_and_allocate(size, is_tlab);
        }
      }
      assert(result == NULL || is_in_reserved(result), "result not in heap");
      return result;
    }

    4.1.3 策略

    定位:观察 CMS GC 触发时间点 Old/MetaSpace 区的 committed 占比是不是一个固定的值,或者像上文提到的观察总的内存使用率也可以。

    解决:尽量将成对出现的空间大小配置参数设置成固定的,如 -Xms 和 -Xmx-XX:MaxNewSize 和 -XX:NewSize-XX:MetaSpaceSize 和 -XX:MaxMetaSpaceSize 等。

     

    4.2 场景二:MetaSpace 区 OOM

    4.2.1 现象

    JVM 在启动后或者某个时间点开始,MetaSpace 的已使用大小在持续增长,同时每次 GC 也无法释放,调大 MetaSpace 空间也无法彻底解决

    4.2.2 原因

    在讨论为什么会 OOM 之前,我们先来看一下这个区里面会存什么数据,Java7 之前字符串常量池被放到了 Perm 区,所有被 intern 的 String 都会被存在这里,由于 String.intern 是不受控的,所以 -XX:MaxPermSize 的值也不太好设置,经常会出现 java.lang.OutOfMemoryError: PermGen space 异常,所以在 Java7 之后常量池等字面量(Literal)、类静态变量(Class Static)、符号引用(Symbols Reference)等几项被移到 Heap 中。而 Java8 之后 PermGen 也被移除,取而代之的是 MetaSpace。

    在最底层,JVM 通过 mmap 接口向操作系统申请内存映射,每次申请 2MB 空间,这里是虚拟内存映射,不是真的就消耗了主存的 2MB,只有之后在使用的时候才会真的消耗内存。申请的这些内存放到一个链表中 VirtualSpaceList,作为其中的一个 Node。

    在上层,MetaSpace 主要由 Klass Metaspace 和 NoKlass Metaspace 两大部分组成。

    • Klass MetaSpace: 就是用来存 Klass 的,就是 Class 文件在 JVM 里的运行时数据结构,这部分默认放在 Compressed Class Pointer Space 中,是一块连续的内存区域,紧接着 Heap。Compressed Class Pointer Space 不是必须有的,如果设置了 -XX:-UseCompressedClassPointers,或者 -Xmx 设置大于 32 G,就不会有这块内存,这种情况下 Klass 都会存在 NoKlass Metaspace 里。
    • NoKlass MetaSpace: 专门来存 Klass 相关的其他的内容,比如 Method,ConstantPool 等,可以由多块不连续的内存组成。虽然叫做 NoKlass Metaspace,但是也其实可以存 Klass 的内容,上面已经提到了对应场景。

    具体的定义都可以在源码 shared/vm/memory/metaspace.hpp 中找到:

    class Metaspace : public AllStatic {
    
      friend class MetaspaceShared;
    
     public:
      enum MetadataType {
        ClassType,
        NonClassType,
        MetadataTypeCount
      };
      enum MetaspaceType {
        ZeroMetaspaceType = 0,
        StandardMetaspaceType = ZeroMetaspaceType,
        BootMetaspaceType = StandardMetaspaceType + 1,
        AnonymousMetaspaceType = BootMetaspaceType + 1,
        ReflectionMetaspaceType = AnonymousMetaspaceType + 1,
        MetaspaceTypeCount
      };
    
     private:
    
      // Align up the word size to the allocation word size
      static size_t align_word_size_up(size_t);
    
      // Aligned size of the metaspace.
      static size_t _compressed_class_space_size;
    
      static size_t compressed_class_space_size() {
        return _compressed_class_space_size;
      }
    
      static void set_compressed_class_space_size(size_t size) {
        _compressed_class_space_size = size;
      }
    
      static size_t _first_chunk_word_size;
      static size_t _first_class_chunk_word_size;
    
      static size_t _commit_alignment;
      static size_t _reserve_alignment;
      DEBUG_ONLY(static bool   _frozen;)
    
      // Virtual Space lists for both classes and other metadata
      static metaspace::VirtualSpaceList* _space_list;
      static metaspace::VirtualSpaceList* _class_space_list;
    
      static metaspace::ChunkManager* _chunk_manager_metadata;
      static metaspace::ChunkManager* _chunk_manager_class;
    
      static const MetaspaceTracer* _tracer;
    }

    MetaSpace 的对象为什么无法释放,我们看下面两点:

    • MetaSpace 内存管理: 类和其元数据的生命周期与其对应的类加载器相同,只要类的类加载器是存活的,在 Metaspace 中的类元数据也是存活的,不能被回收。每个加载器有单独的存储空间,通过 ClassLoaderMetaspace 来进行管理 SpaceManager* 的指针,相互隔离的。

    • MetaSpace 弹性伸缩: 由于 MetaSpace 空间和 Heap 并不在一起,所以这块的空间可以不用设置或者单独设置,一般情况下避免 MetaSpace 耗尽 VM 内存都会设置一个 MaxMetaSpaceSize,在运行过程中,如果实际大小小于这个值,JVM 就会通过 -XX:MinMetaspaceFreeRatio 和 -XX:MaxMetaspaceFreeRatio 两个参数动态控制整个 MetaSpace 的大小,具体使用可以看 MetaSpaceGC::compute_new_size() 方法(下方代码),这个方法会在 CMSCollector 和 G1CollectorHeap 等几个收集器执行 GC 时调用。这个里面会根据 used_after_gcMinMetaspaceFreeRatio 和 MaxMetaspaceFreeRatio 这三个值计算出来一个新的 _capacity_until_GC 值(水位线)。然后根据实际的 _capacity_until_GC 值使用 MetaspaceGC::inc_capacity_until_GC() 和 MetaspaceGC::dec_capacity_until_GC() 进行 expand 或 shrink,这个过程也可以参照场景一中的伸缩模型进行理解。

    4.2.3 策略

    了解大概什么原因后,如何定位和解决就很简单了,可以 dump 快照之后通过 JProfiler 或 MAT 观察 Classes 的 Histogram(直方图) 即可,或者直接通过命令即可定位, jcmd 打几次 Histogram 的图,看一下具体是哪个包下的 Class 增加较多就可以定位了。不过有时候也要结合InstBytes、KlassBytes、Bytecodes、MethodAll 等几项指标综合来看下。如下图便是笔者使用 jcmd 排查到一个 Orika 的问题。

    jcmd <PID> GC.class_stats|awk '{print$13}'|sed  's/\(.*\)\.\(.*\)/\1/g'|sort |uniq -c|sort -nrk1

     

     

    4.3 场景三:过早晋升 *

    4.3.1 现象

    这种场景主要发生在分代的收集器上面,专业的术语称为“Premature Promotion”。90% 的对象朝生夕死,只有在 Young 区经历过几次 GC 的洗礼后才会晋升到 Old 区,每经历一次 GC 对象的 GC Age 就会增长 1,最大通过 -XX:MaxTenuringThreshold 来控制。

    过早晋升一般不会直接影响 GC,总会伴随着浮动垃圾、大对象担保失败等问题,但这些问题不是立刻发生的,我们可以观察以下几种现象来判断是否发生了过早晋升。

    分配速率接近于晋升速率,对象晋升年龄较小。

    GC 日志中出现“Desired survivor size 107347968 bytes, new threshold 1(max 6)”等信息,说明此时经历过一次 GC 就会放到 Old 区。

    Full GC 比较频繁,且经历过一次 GC 之后 Old 区的变化比例非常大

    比如说 Old 区触发的回收阈值是 80%,经历过一次 GC 之后下降到了 10%,这就说明 Old 区的 70% 的对象存活时间其实很短,如下图所示,Old 区大小每次 GC 后从 2.1G 回收到 300M,也就是说回收掉了 1.8G 的垃圾,只有 300M 的活跃对象。整个 Heap 目前是 4G,活跃对象只占了不到十分之一。

    过早晋升的危害:

    • Young GC 频繁,总的吞吐量下降。
    • Full GC 频繁,可能会有较大停顿。

    4.3.2 原因

    主要的原因有以下两点:

    • Young/Eden 区过小: 过小的直接后果就是 Eden 被装满的时间变短,本应该回收的对象参与了 GC 并晋升,Young GC 采用的是复制算法,由基础篇我们知道 copying 耗时远大于 mark,也就是 Young GC 耗时本质上就是 copy 的时间(CMS 扫描 Card Table 或 G1 扫描 Remember Set 出问题的情况另说),没来及回收的对象增大了回收的代价,所以 Young GC 时间增加,同时又无法快速释放空间,Young GC 次数也跟着增加。

    • 分配速率过大: 可以观察出问题前后 Mutator 的分配速率,如果有明显波动可以尝试观察网卡流量、存储类中间件慢查询日志等信息,看是否有大量数据被加载到内存中。

    同时无法 GC 掉对象还会带来另外一个问题,引发动态年龄计算:JVM 通过 -XX:MaxTenuringThreshold 参数来控制晋升年龄,每经过一次 GC,年龄就会加一,达到最大年龄就可以进入 Old 区,最大值为 15(因为 JVM 中使用 4 个比特来表示对象的年龄)。设定固定的 MaxTenuringThreshold 值作为晋升条件:

    • MaxTenuringThreshold 如果设置得过大,原本应该晋升的对象一直停留在 Survivor 区,直到 Survivor 区溢出,一旦溢出发生,Eden + Survivor 中对象将不再依据年龄全部提升到 Old 区,这样对象老化的机制就失效了。

    • MaxTenuringThreshold 如果设置得过小,过早晋升即对象不能在 Young 区充分被回收,大量短期对象被晋升到 Old 区,Old 区空间迅速增长,引起频繁的 Major GC,分代回收失去了意义,严重影响 GC 性能。

    相同应用在不同时间的表现不同,特殊任务的执行或者流量成分的变化,都会导致对象的生命周期分布发生波动,那么固定的阈值设定,因为无法动态适应变化,会造成和上面问题,所以 Hotspot 会使用动态计算的方式来调整晋升的阈值。

    4.3.3 策略

    知道问题原因后我们就有解决的方向,如果是 Young/Eden 区过小,我们可以在总的 Heap 内存不变的情况下适当增大 Young 区,具体怎么增加?一般情况下 Old 的大小应当为活跃对象的 2~3 倍左右,考虑到浮动垃圾问题最好在 3 倍左右,剩下的都可以分给 Young 区。

    拿笔者的一次典型过早晋升优化来看,原配置为 Young 1.2G + Old 2.8G,通过观察 CMS GC 的情况找到存活对象大概为 300~400M,于是调整 Old 1.5G 左右,剩下 2.5G 分给 Young 区。仅仅调了一个 Young 区大小参数(-Xmn),整个 JVM 一分钟 Young GC 从 26 次降低到了 11 次,单次时间也没有增加,总的 GC 时间从 1100ms 降低到了 500ms,CMS GC 次数也从 40 分钟左右一次降低到了 7 小时 30 分钟一次。

     

    4.4 场景四:CMS Old GC 频繁*

    4.4.1 现象

    Old 区频繁的做 CMS GC,但是每次耗时不是特别长,整体最大 STW 也在可接受范围内,但由于 GC 太频繁导致吞吐下降比较多。

    4.4.2 原因

    这种情况比较常见,基本都是一次 Young GC 完成后,负责处理 CMS GC 的一个后台线程 concurrentMarkSweepThread 会不断地轮询,使用 shouldConcurrentCollect() 方法做一次检测,判断是否达到了回收条件。如果达到条件,使用 collect_in_background() 启动一次 Background 模式 GC。轮询的判断是使用 sleepBeforeNextCycle() 方法,间隔周期为 -XX:CMSWaitDuration 决定,默认为2s。

    分析其中逻辑判断是否触发 GC,分为以下几种情况:

    • 触发 CMS GC: 通过调用 _collector->collect_in_background() 进行触发 Background GC 。

      • CMS 默认采用 JVM 运行时的统计数据判断是否需要触发 CMS GC,如果需要根据 -XX:CMSInitiatingOccupancyFraction 的值进行判断,需要设置参数 -XX:+UseCMSInitiatingOccupancyOnly

      • 如果开启了 -XX:UseCMSInitiatingOccupancyOnly 参数,判断当前 Old 区使用率是否大于阈值,则触发 CMS GC,该阈值可以通过参数 -XX:CMSInitiatingOccupancyFraction 进行设置,如果没有设置,默认为 92%。

      • 如果之前的 Young GC 失败过,或者下次 Young 区执行 Young GC 可能失败,这两种情况下都需要触发 CMS GC。

      • CMS 默认不会对 MetaSpace 或 Perm 进行垃圾收集,如果希望对这些区域进行垃圾收集,需要设置参数 -XX:+CMSClassUnloadingEnabled

    • 触发 Full GC: 直接进行 Full GC,这种情况到场景七中展开说明。

      • 如果 _full_gc_requested 为真,说明有明确的需求要进行 GC,比如调用 System.gc。

      • 在 Eden 区为对象或 TLAB 分配内存失败,导致一次 Young GC,在 GenCollectorPolicy 类的 satisfy_failed_allocation() 方法中进行判断。

    大家可以看一下源码中的日志打印,通过日志我们就可以比较清楚地知道具体的原因,然后就可以着手分析了。

    4.4.3 策略

    我们这里还是拿最常见的达到回收比例这个场景来说,与过早晋升不同的是这些对象确实存活了一段时间,Survival Time 超过了 TP9999 时间,但是又达不到长期存活,如各种数据库、网络链接,带有失效时间的缓存等。

    处理这种常规内存泄漏问题基本是一个思路,主要步骤如下:

    Dump Diff 和 Leak Suspects 比较直观就不介绍了,这里说下其它几个关键点:

    • 内存 Dump: 使用 jmap、arthas 等 dump 堆进行快照时记得摘掉流量,同时分别在 CMS GC 的发生前后分别 dump 一次
    • 分析 Top Component: 要记得按照对象、类、类加载器、包等多个维度观察 Histogram,同时使用 outgoing 和 incoming 分析关联的对象,另外就是 Soft Reference 和 Weak Reference、Finalizer 等也要看一下。
    • 分析 Unreachable: 重点看一下这个,关注下 Shallow 和 Retained 的大小。如下图所示,笔者之前一次 GC 优化,就根据 Unreachable Objects 发现了 Hystrix 的滑动窗口问题。

     

    4.5 场景五:内存碎片&收集器退化

    4.5.1 现象

    并发的 CMS GC 算法,退化为 Foreground 单线程串行 GC 模式,STW 时间超长,有时会长达十几秒。其中 CMS 收集器退化后单线程串行 GC 算法有两种:

    • 带压缩动作的算法,称为 MSC,上面我们介绍过,使用标记-清理-压缩,单线程全暂停的方式,对整个堆进行垃圾收集,也就是真正意义上的 Full GC,暂停时间要长于普通 CMS。
    • 不带压缩动作的算法,收集 Old 区,和普通的 CMS 算法比较相似,暂停时间相对 MSC 算法短一些。

    4.5.2 原因

    CMS 发生收集器退化主要有以下几种情况:

    晋升失败(Promotion Failed)

    顾名思义,晋升失败就是指在进行 Young GC 时,Survivor 放不下,对象只能放入 Old,但此时 Old 也放不下。直觉上乍一看这种情况可能会经常发生,但其实因为有 concurrentMarkSweepThread 和担保机制的存在,发生的条件是很苛刻的,除非是短时间将 Old 区的剩余空间迅速填满,例如上文中说的动态年龄判断导致的过早晋升(见下文的增量收集担保失败)。另外还有一种情况就是内存碎片导致的 Promotion Failed,Young GC 以为 Old 有足够的空间,结果到分配时,晋级的大对象找不到连续的空间存放。

    使用 CMS 作为 GC 收集器时,运行过一段时间的 Old 区如下图所示,清除算法导致内存出现多段的不连续,出现大量的内存碎片。

    碎片带来了两个问题:

    • 空间分配效率较低:上文已经提到过,如果是连续的空间 JVM 可以通过使用 pointer bumping 的方式来分配,而对于这种有大量碎片的空闲链表则需要逐个访问 freelist 中的项来访问,查找可以存放新建对象的地址。
    • 空间利用效率变低:Young 区晋升的对象大小大于了连续空间的大小,那么将会触发 Promotion Failed ,即使整个 Old 区的容量是足够的,但由于其不连续,也无法存放新对象,也就是本文所说的问题。

    增量收集担保失败

    分配内存失败后,会判断统计得到的 Young GC 晋升到 Old 的平均大小,以及当前 Young 区已使用的大小也就是最大可能晋升的对象大小,是否大于 Old 区的剩余空间。只要 CMS 的剩余空间比前两者的任意一者大,CMS 就认为晋升还是安全的,反之,则代表不安全,不进行Young GC,直接触发Full GC。

    显式 GC

    这种情况参见场景二。

    并发模式失败(Concurrent Mode Failure)

    最后一种情况,也是发生概率较高的一种,在 GC 日志中经常能看到 Concurrent Mode Failure 关键字。这种是由于并发 Background CMS GC 正在执行,同时又有 Young GC 晋升的对象要放入到了 Old 区中,而此时 Old 区空间不足造成的。

    为什么 CMS GC 正在执行还会导致收集器退化呢?主要是由于 CMS 无法处理浮动垃圾(Floating Garbage)引起的。CMS 的并发清理阶段,Mutator 还在运行,因此不断有新的垃圾产生,而这些垃圾不在这次清理标记的范畴里,无法在本次 GC 被清除掉,这些就是浮动垃圾,除此之外在 Remark 之前那些断开引用脱离了读写屏障控制的对象也算浮动垃圾。所以 Old 区回收的阈值不能太高,否则预留的内存空间很可能不够,从而导致 Concurrent Mode Failure 发生。

    4.5.3 策略

    分析到具体原因后,我们就可以针对性解决了,具体思路还是从根因出发,具体解决策略:

    • 内存碎片: 通过配置 -XX:UseCMSCompactAtFullCollection=true 来控制 Full GC的过程中是否进行空间的整理(默认开启,注意是Full GC,不是普通CMS GC),以及 -XX: CMSFullGCsBeforeCompaction=n 来控制多少次 Full GC 后进行一次压缩。

    • 增量收集: 降低触发 CMS GC 的阈值,即参数 -XX:CMSInitiatingOccupancyFraction 的值,让 CMS GC 尽早执行,以保证有足够的连续空间,也减少 Old 区空间的使用大小,另外需要使用 -XX:+UseCMSInitiatingOccupancyOnly 来配合使用,不然 JVM 仅在第一次使用设定值,后续则自动调整。

    • 浮动垃圾: 视情况控制每次晋升对象的大小,或者缩短每次 CMS GC 的时间,必要时可调节 NewRatio 的值。另外就是使用 -XX:+CMSScavengeBeforeRemark 在过程中提前触发一次 Young GC,防止后续晋升过多对象。

     

    5.0 根因鱼骨图

    送上一张问题根因鱼骨图,一般情况下我们在处理一个 GC 问题时,只要能定位到问题的“病灶”,有的放矢,其实就相当于解决了 80%,如果在某些场景下不太好定位,大家可以借助这种根因分析图通过排除法去定位。

    5.1 调优建议

    • Trade Off: 与 CAP 注定要缺一角一样,GC 优化要在延迟(Latency)、吞吐量(Throughput)、容量(Capacity)三者之间进行权衡。

    • 最终手段: GC 发生问题不是一定要对 JVM 的 GC 参数进行调优,大部分情况下是通过 GC 的情况找出一些业务问题,切记上来就对 GC 参数进行调整,当然有明确配置错误的场景除外。

    • 控制变量: 控制变量法是在蒙特卡洛(Monte Carlo)方法中用于减少方差的一种技术方法,我们调优的时候尽量也要使用,每次调优过程尽可能只调整一个变量。

    • 善用搜索: 理论上 99.99% 的 GC 问题基本都被遇到了,我们要学会使用搜索引擎的高级技巧,重点关注 StackOverFlow、Github 上的 Issue、以及各种论坛博客,先看看其他人是怎么解决的,会让解决问题事半功倍。能看到这篇文章,你的搜索能力基本过关了~

    • 调优重点: 总体上来讲,我们开发的过程中遇到的问题类型也基本都符合正态分布,太简单或太复杂的基本遇到的概率很低,笔者这里将中间最重要的三个场景添加了“*”标识,希望阅读完本文之后可以观察下自己负责的系统,是否存在上述问题。

    • GC 参数: 如果堆、栈确实无法第一时间保留,一定要保留 GC 日志,这样我们最起码可以看到 GC Cause,有一个大概的排查方向。关于 GC 日志相关参数,最基本的 -XX:+HeapDumpOnOutOfMemoryError 等一些参数就不再提了,笔者建议添加以下参数,可以提高我们分析问题的效率

     

    展开全文
  • 怎么成为解决问题的高手

    千次阅读 2019-07-12 18:27:59
    前段时间读了吴军的《文明之光》,书中讲到了笛卡尔著名的《方法论》,笛卡尔系统地阐述了科学的研究方法,笛卡尔指出,研究问题的方法应该分四个步骤: 1.不盲从,不接受任何自己不清楚的真理。 对于一个命题要...

    前段时间读了吴军的《文明之光》,书中讲到了笛卡尔著名的《方法论》,笛卡尔系统地阐述了科学的研究方法,笛卡尔指出,研究问题的方法应该分四个步骤:

    1.不盲从,不接受任何自己不清楚的真理。

    对于一个命题要根据自己的判断,确定有无可疑之处,只有那些没有任何可疑之处的命题才是真理。 这就是笛卡尔著名的“怀疑一切”的观点。不管有什么权威的结论,只要没有经过自己的研究,都可以怀疑。例如亚里士多德曾说,重的物体比轻的物体下落速度快,但事实并非如此。

    2.对于复杂的问题,尽量分解为多个简单的小问题来研究,一个一个的分开解决。  这就是我们常说的分析和化繁为简,化整为零。

    3.解决这些小问题时,应该先按照先易后难的次序,逐步解决。

    4.解决每个小问题之后,再综合起来。看看是否彻底解决了原来的问题。

     

    如今不论是在科学研究中,还是在解决复杂的工程问题时,我们都会采用这四个步骤。

    信息产业从业人员可能有这样的体会:做一款产品,先要分解成模块,然后从易到难实现每一个模块,并对模块进行单元测试,之后将各个模块拼成产品,再对产品进行集成测试,确认是否实现了预想的功能。按照这个方法有条不紊地工作,再难的问题也能解决

     

     

    笛卡尔的这套方法本质上就是一套科学的、解决复杂问题的方法论,你可以用它解决任何复杂问题,不管是工作上的问题、还是生活中的问题。恰好最近在“得到”上学了一门课程-《曲凯:怎样成为解决问题的高手》,课程中讲的其实就是如何使用拆解的方法来解决复杂问题。

    经过了这么多年的工作,你会发现自己所做的所有工作、职业,最终都可以落脚到一个点上,就是解决问题。做产品是为了解决用户的某个问题,做战略是为了解决公司的某个问题,做投资可能是为了解决社会的某个问题,创业则是整合所有的资源和人才来解决一个超级复杂的大问题。面对各种各样的复杂问题,其实你拆解它、解决它的思路和手段都是相通的。你有了这样一套底层方法,你就是一个解决问题的高手了。

    很多问题之所以难搞就是因为它特别的宏大而复杂。 复杂问题很难直接解决,我们每天应对的各种复杂的问题,其实都是在下意识的做拆解,然后再一一去解决掉。 但是下意识靠不住啊,如果遇到自己经验以外的复杂问题,你就要有意识的去拆解。

    任何一个要解决的问题,本身都会自带一个结构。本质,是需要养成「框架式思维」的思考方式。

    什么是复杂问题? 掺杂多个维度和变量问题。什么是元问题?那些最本质最细小的待解决问题。

    复杂问题是不可以直接解决的,需要我们将复杂问题一个个拆解成元问题。通过拆解,很多的问题就变得非常落地,变得可以解决。同时,还会变得很有方法、有逻辑。你可以依着逻辑去想你该达到你的目标。

     

    解决问题的方法论:80%的精力去拆解和定位问题,20%的精力提出解决方案。

    第一步:明确和理解问题

    第二步:拆解&定位问题(80%的精力)

    第三步:提供解决方案(20%的精力)

    第四步:总结问题

    这四步的前两步,也就是明确问题和拆分问题,是最重要的。做好这两个步骤之后,解决方案往往会自己浮出水面,非常神奇。学完这四个步骤的你也能马上变得和以前那个一头雾水的自己完全不一样。

    这套方法论不仅能用来帮公司做一个项目方案,也可以用来估算你家门口的煎饼摊每月赚多少钱,甚至还能帮你有计划地追到你的伴侣。我们上面所提到的这些问题都是你听完这次的课程之后能学会解决的。

     

    如何做问题拆解?

    简单的问题和复杂的问题,基本上都是靠「拆」,只有拆了,才能搞清楚,影响问题的因素有什么,才能各个击破。

    最常用的拆解方法有两种:公式拆解法、流程拆解法为啥我会特别强调这两种方法呢?因为,公式拆解法,基本上能解决80%的问题,剩下的问题,则需要通过梳理用户流程梳理来解决。

     

    公式拆解法

    问题:如何提升广告收入

    公式:广告收入=展现量*点击率*每个点击的价格

     

    有了这个公式以后,就相当于一个问题变成了元问题,现在大家就都知道了。我要提高这个部门的广告收入该怎么做呢?要么提高展现量,要么是提高点击率,要么是提高每个点击的价格,就只有这几种方法,非常的清晰可解决。所以,每个复杂问题的背后都有这样可拆分的元问题。而在业务当中,拆分而来的小问题往往还有另一个词,就是KPI。也就是说,拆分问题其实就是老板们每天都在做的事情。

     

    比如说老板的大目标是提高这个公司的营收。那为了实现这个目标,需要做到哪些部分呢?老板就会把这些KPI都拆分开来,然后分别安排给不同的部门和不同的个人。作为个体,可能你分到的那个目标就是该怎么样把点击率提高10%而已。

     

    也就是说,如果你学会了拆解问题,也就学会了用你老板的思维来思考问题。这样也有助于我们反过来把很小的目标去融会贯通,用更高层的那个思维来要求自己。如果你没有掌握这个思维的话,很可能就会被KPI冲昏了头脑。这也是我之前这么多年工作经验总结下来的一个非常有用的教训。

     

    所以,你在工作和生活中也都可以问自己一个问题,这件事情的公式是什么。其实,把一件事情公式化就是最好的拆解一件事情的方式

     

     

     

     

    展开全文
  • 解决评价类问题以及确定各指标的权重时,学会层次分析法能够派上很大的用场,本文详细介绍了层次分析法的原理与应用技巧,讲解了在数学建模中如何运用层次分析解决评价类问题
  • Java中9种常见的CMS GC问题分析解决

    千次阅读 多人点赞 2020-11-12 19:58:21
    总第422篇2020年 第46篇目前,互联网上 Java 的 GC 资料要么是主要讲解理论,要么就是针对单一场景的 GC 问题进行了剖析,对整个体系总结的资料少之又少。前车之鉴,后事之师...
  • 网页制作过程中的普遍问题分析

    千次阅读 2021-06-12 08:16:21
    网页制作过程中的普遍问题分析网页制作是一个需要下苦功夫进行学习与钻研的过程,同时在学习中会积累到很多的经验,下面是小编搜集整理的一篇关于网页制作常见问题探讨的论文范文,欢迎阅读查看。一、引言如今,国家...
  • 今天就给大家说说怎么建立企业级的财务数据分析平台,3分钟就能看完的超强干货,不读你就亏大了。 先从上到CEO,下到小会计都关心的...其实答案就在报表中,学会财务报表分析,就能找到隐藏在报表中的秘密。 一...
  • 解决问题最简单的方法

    千次阅读 2016-08-20 07:09:45
    这是一本小书,只有145页,7万字左右,花2个小时就能读完,但你会学到立马提高你解决问题能力的10多种方法。
  • 目录1 案例背景2 问题确认与指标拆解题3 问题解决思路4 案例实操4.1 利用分组分析找到亏损店铺做营销优化,实验验证结论4.2 运用对比分析解决哪类产品销售好的问题?4.3 利用矩阵关联法找到销量好和利润高的品类...
  • 如何用最短时间学会数据分析

    千次阅读 2022-02-09 00:28:43
    零基础,想学习数据分析的朋友,往往有如下困惑:网上资料一大堆,完全零基础,从哪开始入手懒癌晚期,无法坚持完成学习生活压力太大,想尽快学会数据分析,提升竞争力经常遇到问题,自己研究浪费大量宝...
  • 浅谈实习过程中遇到的问题解决方法在这次的实习中,通过课堂教学和中学生的接触,我学到了很多书本上学不到的知识,掌握了一些教学方法和技巧,慢慢学会了与学生的沟通交流,但在实习的过程中,也发现了一些问题。...
  • 怎么去思考一个问题,提高解决问题的能力 前言: #:本文转发自【半路歌雨】 #:http://blog.jboost.cn/think-like-a-programmer.html #:如有侵权,联系即删 技术人员的价值,不在于你能写出多么优美的代码,也不...
  • 强化学习是如何解决问题的?

    千次阅读 2018-01-16 16:51:53
    强化学习是如何解决问题的?什么是强化学习算法呢,它离我们有多远?2016年和2017年最具影响力的AlphaGo大胜世界围棋冠军李世石和柯洁事件,其核心算法就用到了强化学习算法。相信很多人想了解或者转行研究强化学习...
  • 10分钟也不一定学会的灵敏度分析

    千次阅读 多人点赞 2020-10-31 13:50:22
    2. 灵敏度分析解决问题有怎样的优点?不用该方法还有其他方法吗? 3. 灵敏度分析类的问题有哪几类?相应的要如何解决? 针对问题一: 灵敏度分析类问题用流程图来解释的话大致是这样的: 判断最.
  • 今天给大家介绍几个数据分析模型吧 一、用户价值模型 1、RFM模型 RFM分析是客户关系分析中一种简单实用客户分析方法,他将最近一次消费、消费频率、消费金额这三个要素构成了数据分析最好的指标,衡量客户价值和...
  • 学会这4个Excel实用技巧,数据分析立马高人一等

    千次阅读 多人点赞 2019-11-06 10:22:41
    对于刚进入数据分析行业新手来说,EXCEL可以被当做一款入门的软件。在学习R或Python前,事先掌握一定的EXCEL知识是百利而无一害。EXCEL凭借其功能强大的函数、可视化图表、以及整齐排列的电子表格功能,使你能够快速...
  • 计算机应用毕业论文第八篇:日常工作问题处理中Python程序的运用摘要:Python是一门简单、实用而且有趣的百搭款语言,在Web应用开发、系统网络运维、科学与数字计算、网络编程等领域都有所建树。在计算机语言中...
  • 教学背景及设计说明:谈到求...用“进一法”和“去尾法”解决问题是在学生学习了求积的近似值、小数除法和计算器的简单应用的基础上来展开的,学生在上课之前已经学习了用“四舍五入”的方法求商的近似值,在本节课...
  • 10个前端经常遇到的问题解决方案

    万次阅读 2020-04-09 16:44:37
    作为一个开发了多个 H5 项目的前端工程师,在开发过程中难免会遇到一些兼容性等爬过坑的问题。现在我将这些问题一一汇总一下,并在后面给出坑产生的原理,和现阶段常规的填坑方案。由此来做一个阶段性的总结。 ...
  • JAVA 初级程序员常见问题分析

    万次阅读 多人点赞 2021-12-01 09:58:00
    什么是实的,就是能解决企业开发过程中的问题的技术。目前市场热点技术就是大数据、人工智能;目前企业实在的技术就是互联网架构技术,具体包括:三大框架企业级架构、分布式架构、微服务架构、开发运维一体化架构 3...
  • 两天前,我写了 七天学会「股票数据分析软件」的开发(上),号召大家尝试着写写代码,不知道大家进度如何。 如果存在掌握一种技能的刚需,而且知道正确的学习方法,经过刻意练习,这门技能很快就能玩儿的有模有样...
  • 前端小白如何提高,解决问题

    万次阅读 2019-09-26 16:50:11
    第一,当然是多学习,打牢基础知识,看看csdn等等博客上的文章,没有什么耐心的可以看看视频,推荐网站是bibi上面有大量培训班花钱才能看的视频,自学的缺点在于,有问题没人解决,这个时候就是要找人,可以加qq群,...
  • 一文学会Java死锁和CPU 100% 问题的排查技巧

    千次阅读 多人点赞 2019-07-05 23:18:50
    做一个积极的人 编码、改bug、提升自己 我有一个乐园,面向编程...文章目录00 本文简介01 Java死锁排查和解决啥是死锁?为啥子会出现死锁?怎么排查代码中出现了死锁?【重点来了】1、使用 jps + jstack2、使用jcons...
  • 日常工作用Python能解决哪些问题

    千次阅读 2019-09-27 10:55:12
    Python语言相关的岗位非常多,有运维,有自动化测试,有后端开发,有机器学习,如果想要快速上手,并且有不错的就业,那就推荐数据分析。用Python爬取了深圳不同行业数据...
  • 回溯法和分支限界法解决旅行商问题

    千次阅读 多人点赞 2020-10-28 15:17:04
    实验三 旅行商问题一. 实验内容二.实验目的三. 算法描述1、回溯算法描述:2、分支限界法算法描述:四....运用分别编程实现回溯法和分支限界法求 TSP 问题的最优解,分析比较两种算法的时间复杂度并验证分析结果。
  • 前两天有个干运营的妹子找我聊天,也是问我数据分析岗位前景的问题,看在妹子长得实在好看的份上,我花了一晚上时间,去智联招聘上面爬了一下数据分析岗位的招聘数据,对数据分析岗位的薪水,需求情况,以及要求的...
  • 数据分析的重要性

    千次阅读 2022-03-15 15:19:13
    产品需要利用数据分析解决业务增长需求。无论你处于什么岗位,具备数据分析思维后,可以利用数据挖掘业务价值,也可以更宏观的审视公司业务创造更高的个人价值。 二.哪些岗位会用到数据分析这项技能 目前国内的很多...
  • 1小时轻松学会用Excel做数据分析

    千次阅读 2017-03-21 14:42:23
    1小时轻松学会用Excel做数据分析

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 131,410
精华内容 52,564
关键字:

学会分析问题解决问题