Menu

  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay
  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay

使用Eclipse Memory Analyzer分析JVM堆Dump

5
Mar
2018

使用Eclipse Memory Analyzer分析JVM堆Dump

By Alex
/ in Java
/ tags JVM, 性能剖析
0 Comments
简介

Eclipse Memory Analyzer(MAT)是一个图形化的Java堆分析工具,速度快且特性丰富,可以用于取代JDK自带的堆Dump浏览器(jhat)。使用MAT,你可以快速分析包含上亿对象的生产环境Dump文件,快速计算某种对象导致的内存占用量,进而评估内存泄漏风险。

你可以将MAT安装为独立运行的RCP应用,也可以作为插件安装到现有的Eclipse中。

jhat

jhat不是本文的主题,这里做一个简单的介绍。

简介

这是JVM自带的堆Dump分析工具,执行如下命令即可使用:

Shell
1
jhat a.map  # a.map为Dump文件路径

jhat默认在7000端口暴露HTTP服务,你可以通过浏览器进行以下查询:

查询 说明
All classes including platform 显示堆中所有类型(Java类)的列表,这些类被加载到JVM中
All Classes excluding platform 类似上面,但是不包括平台(即JDK内部)的类
Show all members of the rootset

显示所有GC Root的对外引用,按照以下几类GC Root,分段展示:

  1. Java类的静态成员
  2. Java局部变量(显示持有这些本地变量的线程)
  3. Native全局变量
  4. Native局部变量
Show instance counts for all classes (including platform) 显示所有类型的实例计数(包含平台类)
Show instance counts for all classes (excluding platform) 显示所有类型的实例计数(不包含平台类)
Show heap histogram

显示一个表格,统计各类型的实例数量、内存占用,按内存占用降序排列

内存占用显示为Shallow size,即对象自身的内存占用。除了数组、字符串等之外的对象Shallow size通常很小,通过引用(指针)关联的其它对象不计入Shallow size,仅仅计入固定尺寸的指针大小

示例应用
代码

下面是一段急剧消耗内存的示例代码:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package com.dangdang.digital.test;
 
import org.apache.commons.lang.math.RandomUtils;
 
import java.util.concurrent.TimeUnit;
 
public class BusyApplication {
 
    public static void main( String[] args ) {
        new MemoryPredator( Integer.parseInt( args[0] ), Integer.parseInt( args[1] ) ).start();
    }
 
}
 
class MemoryPredator extends Thread {
 
    private final int consumeInterval;
 
    private int retainTime;
 
    public MemoryPredator( int consumeInterval, int retainTime ) {
        this.consumeInterval = consumeInterval;
        this.retainTime = retainTime;
    }
 
    @Override
    public void run() {
        for ( ; ; ) {
            consumeMemory( retainTime );
            try {
                TimeUnit.MILLISECONDS.sleep( consumeInterval );
            } catch ( InterruptedException e ) {
            }
        }
    }
 
    private void consumeMemory( final int retainTime ) {
        new MemoryPreyThread( retainTime ).start();
    }
 
}
 
class MemoryPreyThread extends Thread {
 
    private final int retainTime;
 
    public MemoryPreyThread( int retainTime ) {
        this.retainTime = retainTime;
    }
 
    @Override
    public void run() {
        int n = ( RandomUtils.nextInt( 100 ) + 1 );
        Object bigObj = createBigObject( n );
        System.out.println( String.format( "%d MB consumed", n ) );
        try {
            TimeUnit.MILLISECONDS.sleep( retainTime );
        } catch ( InterruptedException e ) {
        }
    }
 
    private Object createBigObject( int n ) {
        Object big = new byte[n * 1024 * 1024];
        return big;
    }
}

让上述程序执行一段时间(应用程序参数使用500 7000),然后使用下面的命令获取堆Dump:

Shell
1
2
# 只Dump可达对象,使用二进制格式,输出为bzapp.hprof
jmap -dump:live,format=b,file=bzapp.hprof  `jps | grep BusyApplication | cut -d' ' -f1`
分析

通过jhat开启服务后,打开浏览器,首先看到的是堆中包含的类的列表(不包含平台类):

1
2
3
4
5
6
7
8
9
10
11
12
13
All Classes (excluding platform)
 
Package com.dangdang.digital.test
class com.dangdang.digital.test.BusyApplication [0xc0005048]
class com.dangdang.digital.test.MemoryPredator [0xc0004ff0]
class com.dangdang.digital.test.MemoryPreyThread [0xc0004f98]
 
Package com.jarego.jayatana
class com.jarego.jayatana.Agent [0xc0005200]
 
Package org.apache.commons.lang.math
class org.apache.commons.lang.math.JVMRandom [0xc0004e60]
class org.apache.commons.lang.math.RandomUtils [0xc0004f00]

点击界面下方的Show heap histogram连接,可以看到Shallow size最大的类型:

1
2
3
4
5
6
7
8
9
10
11
12
Class                                    Instance Count        Total Size
 
class [B                                 1063                  788662183
class [I                                 512                   6789172
class [C                                 2552                  349224
class java.lang.Class                    566                   54336
class [Ljava.lang.Object;                467                   48496
class [S                                 770                   44518
class java.lang.String                   2494                  39904
class java.net.URL                       346                   30448
class [Ljava.util.WeakHashMap$Entry;     156                   22464
class java.util.HashMap$Entry            771                   21588

可以看到byte[]类型,即[B,占用绝大部分内存,接近800MB。

点击class [B上的链接,进入byte[]类型的主页面“Class 0xc009a858”,后面的是类对象的内存地址,和Dump分析关系不大。留意“References to this object”这一段内容:

1
2
3
4
5
6
7
8
9
[B@0xbb0b6e18 (240 bytes) : ??
[B@0xc00bad20 (30 bytes) : ??
[B@0xbae503c0 (29 bytes) : ??
[B@0xbb091708 (115 bytes) : ??
[B@0xbb057350 (29 bytes) : ??
 
...
 
[B@0xc87c6368 (15728656 bytes) : ??       # 大对象

任何类型的示例,都会持有该类型的Class对象的引用,byte[]也不例外。所有存活的byte[]对象因此列于“References to this object”中。我们需要关心的通常(取决于应用场景)是大对象,简单的滚动页面,留意那些更长的行,就可以定位到大的byte[]了。

可以看到对象[B@0xc87c6368占据了15M的内存空间,点击其上的链接,进入[B@0xc87c6368对象的主页面。在页面的下方,可以看到Other Queries - Reference Chains from Rootset - Exclude weak refs,点击后,可以查看从GC Root到此对象的引用链页面:

1
2
3
4
5
6
7
References to [B@0xc87c6368 (15728656 bytes)
 
Java Local References
 
Java Local Reference (from com.dangdang.digital.test.MemoryPreyThread@0xc4ec6208) : # 这是GCRoot
 
--> [B@0xc87c6368 (15728656 bytes)

至此,我们可以大概判定,引发内存问题的代码,位于类MemoryPreyThread中。

MAT界面
首页

点击菜单项 File ⇨  Open Heap Dump ...,看到如下界面:

mat-ui

Inspector面板

上半部分显示当前正在分析的对象的基本信息,以上面的截图为例:

值 意义
0xcb4c69f8
对象的地址
MemoryPreyThread 对象的类型
com.dangdang.digital.test 对象的包名
class com.dangdang.digital.test.MemoryPreyThread @ 0xc0004f98
对象的类型,以及类对象的地址
java.lang.Thread 对象的父类型
sun.misc.Launcher$AppClassLoader @ 0xc0000bd8 加载对象的类的ClassLoader
112(shadlow size) 此对象本身的大小
104,857,784(retained size) 此对象本身的大小 + 此对象引用(包括间接)对象的大小
GC root: Native Stack,Thread 提示此对象是否GC Root

下半部分包括4个选项卡:

  1. Statics:此对象的类变量列表
  2. Attributes:此对象的实例变量列表
  3. Class Hierarchy:类层次图
  4. Value:对象的值
Overview面板

此面板使用饼图显示Retained Size最大的对象,并列出其导致的内存驻留的尺寸。这些对象往往就是问题所在。

饼图上方的按钮区、和饼图下方的Actions/Reports/Step By Step,功能是对应的。

其中Actions包括:

  1. Histogram:类似jhat的Histogram类似,但是支持统计Retained Size,支持排序
  2. Dominator Tree:显示那些导致内存驻留最严重的对象,按对象导致驻留的比例,降序排列。可以展开这些对象的引用链
  3. Top Consumers:以饼图、列表方式显示导致内存驻留最严重的对象、类、类加载器、包
  4. Duplicate Classes:显示不同类加载器重复加载的类

其中Reports包括:

  1. Leak Suspects:可以的内存泄漏报告,更加细粒度的、辅助人工分析的报告
使用MAT分析示例应用

使用jhat,我们就可以获取堆Dump中的全部信息了。 但是其UI非常简陋,缺乏排序、统计类功能,这些功能正是MAT能提供的。下面我们使用MAT来分析上一章的示例应用。

打开Dump后,在Overview面板的饼图上,可以立即看到所有大内存驻留都是由com.dangdang.digital.test.MemoryPreyThread类型的对象所引发。

单击任何一个饼图切片,点击菜单List Objects ⇨ With Outgoing References,可以看到对象引用的哪些对象的Shallow Size较大。这些对象就是内存驻留的根源:

mat-list-object

很明显, MemoryPreyThread所引用的、byte[]类型的局部变量,其Shallow Size占据了全部内存份额。注意:如果上级对象的Outgoing引用特别多,则问题所在的子级对象可能看不到,这种情况下可以点击Retained Heap列排序或者手工展开。

对于像示例应用这样简单的代码,分析到这里就直接能定位到问题代码所在行号了。生产环境中的情况要复杂的多,单纯基于堆内存Dump进行静态分析不一定能奏效,需要结合线程栈Dump,甚至是剖析工具(JMC、JProfiler等)录制的内存分配历史。

实例
单个线程消耗6G内存

概要情况如下图:

mat-5g-1

某个线程引用了接近6G的数据。从线程名字上来看,它是一个普通的工作线程,持有的6G内存应该是某种临时性对象。

分析此线程的Outgoing引用,排序Retained Heap列,将大内存条目显示出来:

可以看到,内存由一个局部变量(<Java Local>)导致。需要注意,局部变量的GC Root都是当前执行线程。出现问题的变量引用一个ArrayList对象,进一步展开可以猜测到此对象是数据库查询的结果集:

mat-5g-2

可以看到,此ArrayList的元素多达1000万。很可能是错误的SQL语句把大量行甚至整个表都读取过来,一下子把JVM内存撑爆了。

展开一个ByteArrayRow,查看行中的元素,进一步定位到所操作的表。配合JMC查看Hot Methods,定位到了存在问题的方法。

Dubbo消费者内存溢出
概要情况

线上环境某服务,因为内存溢出、连续FGC被监控进程杀死,我们获得的堆Dump,内存占用总计1.7GB,Netty 3.x中的类NioClientSocketPipelineSink导致了绝大部分的内存驻留。

nio-client-socket-pipeline-sink

IncomingRef分析

点击饼图中最大的切片NioClientSocketPipelineSink进行分析,打开菜单项List Objects ⇨ With Incoming References,展开:

nio-client-socket-pipeline-sink-ir

可以看到NioClientSocketPipelineSink是Netty处理管线(Pipeline)的一部分。在很多框架中,管线常常和SRC、SINK等术语一起出现,SRC常常表示管线的输入、SINK则表示输出。

一个全局性的ChannelFactory、大量DefaultChannelPipeline通过sink属性引用了NioClientSocketPipelineSink。这些DefaultChannelPipeline应该是某种一次性对象,不熟悉代码的话只能猜测到这个程度,可以考虑通过JMC来录制对象分配的调用栈,来证实这种猜测。

那么,谁创建了这些DefaultChannelPipeline呢?从上面的截图中可以看到Dubbo的NettyClient间接引用了NioClientSocketPipelineSink,再随机抽取几个DefaultChannelPipeline展开:

nio-client-socket-pipeline-sink-ir2

可以看到来自Dubbo的NettyClient通过channel ⇨ pipeline ⇨ sink 引用了DefaultChannelPipeline。com.alibaba.dubbo上面引用链上唯一的非Netty包,我们有理由猜测Dubbo和内存溢出有关。

我们可以看一下相关对象的计数。对于希望统计数量的对象,在调用链上点选它,左下面板切换到Class Hierarchy,在类名上右击,选择List Object  ⇨ With Incoming References:

incoming-ref-of-class

 

由于类的每个对象都会引用类对象,因此展开右侧窗口的根节点,即可知晓对象的大概数量。在本例中,DefaultChannelPipeline、NioClientSocketChannel的数量大概是130万,NettyClient的数量是33。

DominatorTree分析

MAT的支配树功能,可以帮助你追踪实际(Shallow)消耗内存的对象:

pipelinesink-dominator-tree

可以看到,Netty内部的某种链表结构(LinkedTransferQueue)的每个元素的RegisterTask对象占用了1KB的内存,但是链表长度非常大,因此这个链表消耗了大量的内存。

从名字LinkedTransferQueue可以猜测,这是Netty用来存储待处理的网络数据的。

代码分析

从MAT分析的结果来看,NioClientSocketPipelineSink的内部类Boss的实例变量registerTaskQueue,占据了高达1.64GB的内存。我们来看一下这个类是做什么的:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// 代码中不相关的部分做了删减
class NioClientSocketPipelineSink extends AbstractChannelSink {
 
    final Executor bossExecutor;
 
    private final Boss[] bosses;
    private final NioWorker[] workers;
 
    private final AtomicInteger bossIndex = new AtomicInteger();
    private final AtomicInteger workerIndex = new AtomicInteger();
 
    // 这是外部能影响队列registerTaskQueue的唯一入口
    public void eventSunk( ChannelPipeline pipeline, ChannelEvent e) throws Exception {
        if (e instanceof ChannelStateEvent) {
 
            ChannelState state = event.getState();
            Object value = event.getValue();
 
            switch (state) {
            case CONNECTED:
                if (value != null) {
                    // 连接到客户端通道
                    connect(channel, future, (SocketAddress) value);
                } else {
                    channel.worker.close(channel, future);
                }
                break;
        } else if (e instanceof MessageEvent) {}
    }
 
 
    private void connect(  final NioClientSocketChannel channel, final ChannelFuture cf, SocketAddress remoteAddress) {
        try {
            if (channel.socket.connect(remoteAddress)) {
                channel.worker.register(channel, cf);
            } else {
                // 通道是非阻塞模式,因此会走到这个分支
                nextBoss().register(channel);
            }
 
        } catch (Throwable t) {}
    }
    Boss nextBoss() {
        return bosses[Math.abs( bossIndex.getAndIncrement() % bosses.length)];
    }
 
 
    private final class Boss implements Runnable {
 
        private final AtomicBoolean wakenUp = new AtomicBoolean();
        private final Object startStopLock = new Object();
        private final Queue<Runnable> registerTaskQueue = new LinkedTransferQueue<Runnable>();
 
        // 这个方法直接导致registerTaskQueue增大
        void register(NioClientSocketChannel channel) {
            Runnable registerTask = new RegisterTask(this, channel);
 
            synchronized (startStopLock) {
                // 如果尚未启动,则使用Executor启动Client Boss线程
                // 每当有新的连接请求到达,都会尝试启动Client Boss线程
                if (!started) {
                    DeadLockProofWorker.start(
                                bossExecutor,
                                new ThreadRenamingRunnable(
                                        this, "New I/O client boss #" + id + '-' + subId));
                }
                // 添加一个RegisterTask
                boolean offered = registerTaskQueue.offer(registerTask);
            }
 
            if (wakenUp.compareAndSet(false, true)) {
                // 唤醒选择器
                selector.wakeup();
            }
        }
 
        public void run() {
            Selector selector = this.selector;
            long lastConnectTimeoutCheckTimeNanos = System.nanoTime();
            for (;;) {
                wakenUp.set(false);
 
                try {
                    // 最多阻塞500ms
                    int selectedKeyCount = selector.select(500);
                    if (wakenUp.get()) {
                        selector.wakeup();
                    }
                    // 消费registerTaskQueue队列
                    processRegisterTaskQueue();
 
                    
                    if (selector.keys().isEmpty()) {
                        if (shutdown ||
                            bossExecutor instanceof ExecutorService && ((ExecutorService) bossExecutor).isShutdown()) {
 
                            synchronized (startStopLock) {
                                if (registerTaskQueue.isEmpty() && selector.keys().isEmpty()) {
                                    // 标记Client Boss线程为停止状态,下次再有连接请求到达,会再次启动
                                    started = false;
                                    try {
                                        selector.close();
                                    } catch (IOException e) {
                                        logger.warn(
                                                "Failed to close a selector.", e);
                                    } finally {
                                        this.selector = null;
                                    }
                                    // 退出条件
                                    break;
                                } else {
                                    shutdown = false;
                                }
                            }
                        } else {
                            shutdown = true;
                        }
                    } else {
                        shutdown = false;
                    }
 
                } catch (Throwable t) {}
            }
        }
 
        private void processRegisterTaskQueue() {
            for (;;) {
                final Runnable task = registerTaskQueue.poll();
                if (task == null) {
                    break;
                }
 
                task.run();
            }
        }
 
    private static final class RegisterTask implements Runnable {
 
        RegisterTask(Boss boss, NioClientSocketChannel channel) {
            this.boss = boss;
            this.channel = channel;
        }
 
        public void run() {
            try {
                // 在指定的选择器上注册通道的事件
                channel.socket.register( boss.selector, SelectionKey.OP_CONNECT, channel);
            } catch (ClosedChannelException e) {
                channel.worker.close(channel, succeededFuture(channel));
            }
 
            int connectTimeout = channel.getConfig().getConnectTimeoutMillis();
            if (connectTimeout > 0) {
                channel.connectDeadlineNanos = System.nanoTime() + connectTimeout * 1000000L;
            }
        }
    }
}

可以看到:

  1. 每当有新的连接请求时,都会调用Boss.register,从而唤醒Boss.run线程
  2. 一旦Boss.run线程线程启动,它就会消费掉所有堆积的registerTaskQueue元素

也就是说,正常情况下,registerTaskQueue队列不应该出现堆积的。由于发现问题时没有开启Dubbo或Netty的日志,准备等待下次问题重现后继续深入分析。

← 基于Rook的Kubernetes存储方案
Logback学习笔记 →

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

Related Posts

  • 使用Oracle Java Mission Control监控JVM运行状态
  • JVM参数与性能调优
  • 使用Sysdig进行系统性能分析
  • Go应用性能剖析
  • 使用Chrome开发者工具分析内存泄漏

Recent Posts

  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
  • A Comprehensive Study of Kotlin for Java Developers
  • 背诵营笔记
  • 利用LangChain和语言模型交互
  • 享学营笔记
ABOUT ME

汪震 | Alex Wong

江苏淮安人,现居北京。目前供职于腾讯云,专注容器方向。

GitHub:gmemcc

Git:git.gmem.cc

Email:gmemjunk@gmem.cc@me.com

ABOUT GMEM

绿色记忆是我的个人网站,域名gmem.cc中G是Green的简写,MEM是Memory的简写,CC则是我的小天使彩彩名字的简写。

我在这里记录自己的工作与生活,同时和大家分享一些编程方面的知识。

GMEM HISTORY
v2.00:微风
v1.03:单车旅行
v1.02:夏日版
v1.01:未完成
v0.10:彩虹天堂
v0.01:阳光海岸
MIRROR INFO
Meta
  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org
Recent Posts
  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
    In this blog post, I will walk ...
  • A Comprehensive Study of Kotlin for Java Developers
    Introduction Purpose of the Study Understanding the Mo ...
  • 背诵营笔记
    Day 1 Find Your Greatness 原文 Greatness. It’s just ...
  • 利用LangChain和语言模型交互
    LangChain是什么 从名字上可以看出来,LangChain可以用来构建自然语言处理能力的链条。它是一个库 ...
  • 享学营笔记
    Unit 1 At home Lesson 1 In the ...
  • K8S集群跨云迁移
    要将K8S集群从一个云服务商迁移到另外一个,需要解决以下问题: 各种K8S资源的迁移 工作负载所挂载的数 ...
  • Terraform快速参考
    简介 Terraform用于实现基础设施即代码(infrastructure as code)—— 通过代码( ...
  • 草缸2021
    经过四个多月的努力,我的小小荷兰景到达极致了状态。

  • 编写Kubernetes风格的APIServer
    背景 前段时间接到一个需求做一个工具,工具将在K8S中运行。需求很适合用控制器模式实现,很自然的就基于kube ...
  • 记录一次KeyDB缓慢的定位过程
    环境说明 运行环境 这个问题出现在一套搭建在虚拟机上的Kubernetes 1.18集群上。集群有三个节点: ...
  • eBPF学习笔记
    简介 BPF,即Berkeley Packet Filter,是一个古老的网络封包过滤机制。它允许从用户空间注 ...
  • IPVS模式下ClusterIP泄露宿主机端口的问题
    问题 在一个启用了IPVS模式kube-proxy的K8S集群中,运行着一个Docker Registry服务 ...
  • 念爷爷
      今天是爷爷的头七,十二月七日、阴历十月廿三中午,老人家与世长辞。   九月初,回家看望刚动完手术的爸爸,发

  • 6 杨梅坑

  • liuhuashan
    深圳人才公园的网红景点 —— 流花山

  • 1 2020年10月拈花湾

  • 内核缺陷触发的NodePort服务63秒延迟问题
    现象 我们有一个新创建的TKE 1.3.0集群,使用基于Galaxy + Flannel(VXLAN模式)的容 ...
  • Galaxy学习笔记
    简介 Galaxy是TKEStack的一个网络组件,支持为TKE集群提供Overlay/Underlay容器网 ...
TOPLINKS
  • Zitahli's blue 91 people like this
  • 梦中的婚礼 64 people like this
  • 汪静好 61 people like this
  • 那年我一岁 36 people like this
  • 为了爱 28 people like this
  • 小绿彩 26 people like this
  • 彩虹姐姐的笑脸 24 people like this
  • 杨梅坑 6 people like this
  • 亚龙湾之旅 1 people like this
  • 汪昌博 people like this
  • 2013年11月香山 10 people like this
  • 2013年7月秦皇岛 6 people like this
  • 2013年6月蓟县盘山 5 people like this
  • 2013年2月梅花山 2 people like this
  • 2013年淮阴自贡迎春灯会 3 people like this
  • 2012年镇江金山游 1 people like this
  • 2012年徽杭古道 9 people like this
  • 2011年清明节后扬州行 1 people like this
  • 2008年十一云龙公园 5 people like this
  • 2008年之秋忆 7 people like this
  • 老照片 13 people like this
  • 火一样的六月 16 people like this
  • 发黄的相片 3 people like this
  • Cesium学习笔记 90 people like this
  • IntelliJ IDEA知识集锦 59 people like this
  • 基于Kurento搭建WebRTC服务器 38 people like this
  • Bazel学习笔记 37 people like this
  • PhoneGap学习笔记 32 people like this
  • NaCl学习笔记 32 people like this
  • 使用Oracle Java Mission Control监控JVM运行状态 29 people like this
  • Ceph学习笔记 27 people like this
  • 基于Calico的CNI 27 people like this
Tag Cloud
ActiveMQ AspectJ CDT Ceph Chrome CNI Command Cordova Coroutine CXF Cygwin DNS Docker eBPF Eclipse ExtJS F7 FAQ Groovy Hibernate HTTP IntelliJ IO编程 IPVS JacksonJSON JMS JSON JVM K8S kernel LB libvirt Linux知识 Linux编程 LOG Maven MinGW Mock Monitoring Multimedia MVC MySQL netfs Netty Nginx NIO Node.js NoSQL Oracle PDT PHP Redis RPC Scheduler ServiceMesh SNMP Spring SSL svn Tomcat TSDB Ubuntu WebGL WebRTC WebService WebSocket wxWidgets XDebug XML XPath XRM ZooKeeper 亚龙湾 单元测试 学习笔记 实时处理 并发编程 彩姐 性能剖析 性能调优 文本处理 新特性 架构模式 系统编程 网络编程 视频监控 设计模式 远程调试 配置文件 齐塔莉
Recent Comments
  • qg on Istio中的透明代理问题
  • heao on 基于本地gRPC的Go插件系统
  • 黄豆豆 on Ginkgo学习笔记
  • cloud on OpenStack学习笔记
  • 5dragoncon on Cilium学习笔记
  • Archeb on 重温iptables
  • C/C++编程:WebSocketpp(Linux + Clion + boostAsio) – 源码巴士 on 基于C/C++的WebSocket库
  • jerbin on eBPF学习笔记
  • point on Istio中的透明代理问题
  • G on Istio中的透明代理问题
  • 绿色记忆:Go语言单元测试和仿冒 on Ginkgo学习笔记
  • point on Istio中的透明代理问题
  • 【Maven】maven插件开发实战 – IT汇 on Maven插件开发
  • chenlx on eBPF学习笔记
  • Alex on eBPF学习笔记
  • CFC4N on eBPF学习笔记
  • 李运田 on 念爷爷
  • yongman on 记录一次KeyDB缓慢的定位过程
  • Alex on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • haolipeng on 基于本地gRPC的Go插件系统
  • 吴杰 on 基于C/C++的WebSocket库
©2005-2025 Gmem.cc | Powered by WordPress | 京ICP备18007345号-2