一,什么是内存泄漏

Go 中的并发性是以 goroutine(独立活动)和 channel(用于通信)的形式实现的。处理 goroutine 时,程序员需要小心翼翼地避免泄露。如果最终永远堵塞在 I/O 上(例如 channel 通信),或者陷入死循环,那么 goroutine 会发生泄露。即使是阻塞的 goroutine,也会消耗资源,因此,程序可能会使用比实际需要更多的内存,或者最终耗尽内存,从而导致崩溃。虽然我们知道goroutine在初始化的时候会会分配一个2kb的栈地址空间,(关于内存分配的问题可以参考http://ifeve.com/memory-barriers-or-fences/)但是如果大量的goroutine被阻塞,造成的内存浪费也是客观的。

我们知道go采用的gc是标记回收法:
从根变量来时遍历所有被引用对象,标记之后进行清除操作,对未标记对象进行回收。
阻塞状态是go调度的一个待唤醒的状态,是不能被gc的。

我们知道:
如向channel发送数据的时候,该goroutine会一直阻塞直到另一个goroutine接受该channel的数据,反之亦然,goroutine接受channel的数据的时候也会一直阻塞直到另一个goroutine向该channel发送数据

发送端的channel满了,那么发送端将处于阻塞状态知道里面的消息被消费

ch := make(chan int)
go func() {
ch <- 1
fmt.Println(111)
}()

接受端消费channel的时候发现为空

ch := make(chan int, 1)
go func() {
<-ch
fmt.Println(111)
}()

上述的两种中状态就是最简单的goroutine阻塞的情况,我们遇到goroutine阻塞,从而导致的内存泄漏的情况无非就是这样的。

内存泄漏是如何产生的呢?

1、发送一个没有接受者的channel

func query() int {
n := rand.Intn(100)
time.Sleep(time.Duration(n) * time.Millisecond)
return n
} func queryAll() int {
ch := make(chan int)
go func() { ch <- query() }()
go func() { ch <- query() }()
go func() { ch <- query() }()
return <-ch
} func main() {
for i := 0; i < 4; i++ {
queryAll()
fmt.Printf("#goroutines: %d", runtime.NumGoroutine())
}
}

输出:

#goroutines: 3
#goroutines: 5
#goroutines: 7
#goroutines: 9

每次调用 queryAll 后,goroutine 的数目会发生增长。问题在于,在接收到第一个响应后,“较慢的” goroutine 将会发送到另一端没有接收者的 channel 中。

2、nil channel

写入到nil channel会永远阻塞

package main

func main() {
var ch chan struct{}
ch <- struct{}{}
}

所以它导致死锁:

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send (nil chan)]:
main.main()
...

当从 nil channel 读取数据时,同样的事情发生了:
var ch chan struct{}
<-ch

传递尚未初始化的channel,也可能发生

func main() {
var ch chan int
if false {
ch = make(chan int, 1)
ch <- 1
}
go func(ch chan int) {
<-ch
}(ch) c := time.Tick(1 * time.Second)
for range c {
fmt.Printf("#goroutines: %d", runtime.NumGoroutine())
}
}

3、channel通讯超时

如果goroutine在通讯的时候,发送端的channel由于某种原因没有到达消费端的goroutine,那么下游消费的goroutine就会长时间处于阻塞的状态等待消息的唤醒。

/*
检查channel读写超时,并做超时的处理
*/
func testTimeout() {
g := make(chan int)
quit := make(chan bool) go func() {
for {
select {
case v := <-g:
fmt.Println(v)
case <-time.After(time.Second * time.Duration(3)):
quit <- true
fmt.Println("超时,通知主线程退出")
return
}
}
}() for i := 0; i < 3; i++ {
g <- i
} <-quit
fmt.Println("收到退出通知,主线程退出")
}

二,如何排查内存的泄漏

我们可以使用pprof进行排查

那我们就来了解下pprof的基本知识点

什么是pprof

pprof是Go的性能分析工具,在程序运行过程中,可以记录程序的运行信息,可以是CPU使用情况、内存使用情况、goroutine运行情况等,当需要性能调优或者定位Bug时候,这些记录的信息是相当重要。

基本使用

使用pprof有多种方式,Go已经现成封装好了1个:net/http/pprof,使用简单的几行命令,就可以开启pprof,记录运行信息,并且提供了Web服务,能够通过浏览器和命令行2种方式获取运行数据。

import (
"fmt"
"net/http"
_ "net/http/pprof"
) func main() {
// 开启pprof,监听请求
ip := "127.0.0.1:6060"
if err := http.ListenAndServe(ip, nil); err != nil {
fmt.Printf("start pprof failed on %s\n", ip)
}
}

我们输入ip:port/debug/pprof/打开pprof主页
例如我的地址
http://127.0.0.1:6060/debug/pprof/

会看到下面的信息

下面来分析一下上面的具体参数的意思

allocs:
A sampling of all past memory allocations
所有过去内存分配的采样
block:
Stack traces that led to blocking on synchronization primitives
导致同步原语阻塞的堆栈跟踪
cmdline:
The command line invocation of the current program
当前程序的命令行调用 goroutine:
Stack traces of all current goroutines
heap:
A sampling of memory allocations of live objects. You can specify the gc GET parameter to run GC before taking the heap sample.
活动对象内存分配的采样。在获取堆样本之前,可以指定gc get参数来运行gc。(也就是堆内存的信息)
mutex:
Stack traces of holders of contended mutexes
争用互斥锁持有者的堆栈跟踪(锁的信息)
profile:
CPU profile. You can specify the duration in the seconds GET parameter. After you get the profile file, use the go tool pprof command to investigate the profile.
CPU配置文件。可以在seconds get参数中指定持续时间。获取配置文件后,使用go tool pprof命令调查配置文件。
threadcreate:
Stack traces that led to the creation of new OS threads
导致创建新操作系统线程的堆栈跟踪(线程的信息)
trace:
A trace of execution of the current program. You can specify the duration in the seconds GET parameter. After you get the trace file, use the go tool trace command to investigate the trace.
对当前程序执行的跟踪。可以在seconds get参数中指定持续时间。获取跟踪文件后,使用go tool trace命令调查跟踪。

命令行方式

当连接在服务器终端上的时候,是没有浏览器可以使用的,Go提供了命令行的方式,能够获取以上5类信息,这种方式用起来更方便。
使用命令go tool pprof url可以获取指定的profile文件,此命令会发起http请求,然后下载数据到本地,之后进入交互式模式,就像gdb一样,可以使用命令查看运行信息,以下是5类请求的方式:

# 下载cpu profile,默认从当前开始收集30s的cpu使用情况,需要等待30s
go tool pprof http://localhost:6060/debug/pprof/profile # 30-second CPU profile
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=120 # wait 120s # 下载heap profile
go tool pprof http://localhost:6060/debug/pprof/heap # heap profile # 下载goroutine profile
go tool pprof http://localhost:6060/debug/pprof/goroutine # goroutine profile # 下载block profile
go tool pprof http://localhost:6060/debug/pprof/block # goroutine blocking profile # 下载mutex profile
go tool pprof http://localhost:6060/debug/pprof/mutex

内存泄露的发现

如果使用云平台部署Go程序,云平台都提供了内存查看的工具,可以查看OS的内存占用情况和某个进程的内存占用情况,比如阿里云,我们在1个云主机上只部署了1个Go服务,所以OS的内存占用情况,基本是也反映了进程内存占用情况,OS内存占用情况如下,可以看到随着时间的推进,内存的占用率在不断的提高,这是内存泄露的最明显现象:

怎么用heap发现内存问题
使用pprof的heap能够获取程序运行时的内存信息,在程序平稳运行的情况下,每个一段时间使用heap获取内存的profile,然后使用base能够对比两个profile文件的差别,就像diff命令一样显示出增加和减少的变化,使用一个简单的demo来说明heap和base的使用,依然使用demo2进行展示。

// 展示内存增长和pprof,并不是泄露
package main import (
"fmt"
"net/http"
_ "net/http/pprof"
"os"
"time"
) // 运行一段时间:fatal error: runtime: out of memory
func main() {
// 开启pprof
go func() {
ip := "0.0.0.0:6060"
if err := http.ListenAndServe(ip, nil); err != nil {
fmt.Printf("start pprof failed on %s\n", ip)
os.Exit(1)
}
}() tick := time.Tick(time.Second / 100)
var buf []byte
for range tick {
buf = append(buf, make([]byte, 1024*1024)...)
}
}

将上面代码运行起来,执行以下命令获取profile文件,1分钟后再获取1次。
go tool pprof http://localhost:6060/debug/pprof/heap
我已经获取到了两个profile文件:

Administrator@SC-201807230940 MINGW64 ~/pprof
$ ls
pprof.alloc_objects.alloc_space.inuse_objects.inuse_space.001.pb.gz
pprof.alloc_objects.alloc_space.inuse_objects.inuse_space.002.pb.gz

使用base把001文件作为基准,然后用002和001对比,先执行top看top的对比,然后执行list main列出main函数的内存对比,结果如下:

Administrator@SC-201807230940 MINGW64 ~/pprof
$ go tool pprof -base pprof.alloc_objects.alloc_space.inuse_objects.inuse_space.001.pb.gz pprof.alloc_objects.alloc_space.inuse_objects.inuse_space.002.pb.gz

结果

(pprof) top
Showing nodes accounting for 1.04GB, 50.58% of 2.06GB total
flat flat% sum% cum cum%
1.04GB 50.58% 50.58% 1.04GB 50.58% main.main
0 0% 50.58% 1.04GB 50.58% runtime.main
(pprof)

使用traces

Type: inuse_space
Time: Jul 29, 2019 at 8:48am (CST)
-----------+-------------------------------------------------------
bytes: 1.55GB
1.55GB main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 1.24GB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 1016.83MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 813.46MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 902.59kB
0 compress/flate.NewWriter
compress/gzip.(*Writer).Write
runtime/pprof.(*profileBuilder).build
runtime/pprof.writeHeapProto
runtime/pprof.writeHeap
runtime/pprof.(*Profile).WriteTo
net/http/pprof.handler.ServeHTTP
net/http/pprof.Index
net/http.HandlerFunc.ServeHTTP
net/http.(*ServeMux).ServeHTTP
net/http.serverHandler.ServeHTTP
net/http.(*conn).serve
-----------+-------------------------------------------------------
bytes: 650.77MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 520.61MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 416.48MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 333.19MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 266.55MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 213.23MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 170.59MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 136.47MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 109.17MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 87.34MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 69.87MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 55.89MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 44.71MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 35.77MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 28.61MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 22.88MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 18.30MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 14.64MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 11.71MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 9.37MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 7.49MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 5.99MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 4.79MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 3.07MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 2.46MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 1.16MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 1MB
1.16MB main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 520.61MB
-520.61MB main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 416.48MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 333.19MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 266.55MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 213.23MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 170.59MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 136.47MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 109.17MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 87.34MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 69.87MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 55.89MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 44.71MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 35.77MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 28.61MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 22.88MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 18.30MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 14.64MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 11.71MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 9.37MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 7.49MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 5.99MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 4.79MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 3.07MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 2.46MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 1.16MB
0 main.main
runtime.main
-----------+-------------------------------------------------------
bytes: 1MB
-1.16MB main.main
runtime.main
-----------+-------------------------------------------------------
(pprof)
使用list
(pprof) list main.main
Total: 2.06GB
ROUTINE ======================== main.main in D:\gowork\src\study\main\main.go
1.04GB 1.04GB (flat, cum) 50.58% of Total
. . 57: }()
. . 58:
. . 59: tick := time.Tick(time.Second / 100)
. . 60: var buf []byte
. . 61: for range tick {
1.04GB 1.04GB 62: buf = append(buf, make([]byte, 1024*1024)...)
. . 63: }
. . 64:}
. . 65:
. . 66:
. . 67:
(pprof)

heap“不能”定位内存泄露
heap能显示内存的分配情况,以及哪行代码占用了多少内存,我们能轻易的找到占用内存最多的地方,如果这个地方的数值还在不断怎大,基本可以认定这里就是内存泄露的位置。
曾想按图索骥,从内存泄露的位置,根据调用栈向上查找,总能找到内存泄露的原因,这种方案看起来是不错的,但实施起来却找不到内存泄露的原因,结果是事半功倍。
原因在于一个Go程序,其中有大量的goroutine,这其中的调用关系也许有点复杂,也许内存泄露是在某个三方包里。举个栗子,比如下面这幅图,每个椭圆代表1个goroutine,其中的数字为编号,箭头代表调用关系。heap profile显示g111(最下方标红节点)这个协程的代码出现了泄露,任何一个从g101到g111的调用路径都可能造成了g111的内存泄露,有2类可能:
该goroutine只调用了少数几次,但消耗了大量的内存,说明每个goroutine调用都消耗了不少内存,内存泄露的原因基本就在该协程内部。
该goroutine的调用次数非常多,虽然每个协程调用过程中消耗的内存不多,但该调用路径上,协程数量巨大,造成消耗大量的内存,并且这些goroutine由于某种原因无法退出,占用的内存不会释放,内存泄露的原因在到g111调用路径上某段代码实现有问题,造成创建了大量的g111。
第2种情况,就是goroutine泄露,这是通过heap无法发现的,所以heap在定位内存泄露这件事上,发挥的作用不大。

内存泄露的排查

Web可视化查看

Web方式适合web服务器的端口能访问的情况,使用起来方便,有2种方式:
查看某条调用路径上,当前阻塞在此goroutine的数量
查看所有goroutine的运行栈(调用路径),可以显示阻塞在此的时间

import (
"fmt"
"net/http"
_ "net/http/pprof"
"os"
"time"
) func main() {
// 开启pprof
go func() {
ip := "0.0.0.0:6060"
if err := http.ListenAndServe(ip, nil); err != nil {
fmt.Printf("start pprof failed on %s\n", ip)
os.Exit(1)
}
}()
outCh := make(chan int)
for i := 1; i <= 5; i++ {
go func() {
outCh <- 1
}()
time.Sleep(time.Second)
} ///value := <-outCh
//fmt.Println("value : ", value)
//time
time.Sleep(100 * time.Second)
}

方式一

url请求中设置debug=1:

使用http://127.0.0.1:6060/debug/pprof/goroutine?debug=1

我们可以明显的看到有5个goroutine被阻塞了

其实应该是5个地方主goroutine应为时间也是被阻塞掉了

我们看到有5个goroutine被同一个资源阻塞掉了,并且指向的代码块是23行,那么我们就能很快的进行定位排查。

方式二

url请求中设置debug=2:
使用:http://127.0.0.1:6060/debug/pprof/goroutine?debug=2

我们可以看到阻塞的时间,同时也能看到阻塞的代码块

命令行交互式方法

top会列出5个统计数据:
flat: 本函数占用的内存量。
flat%: 本函数内存占使用中内存总量的百分比。
sum%: 前面每一行flat百分比的和,比如第2行虽然的100% 是 100% + 0%。
cum: 是累计量,加入main函数调用了函数f,函数f占用的内存量,也会记进来。
cum%: 是累计量占总量的百分比。
list
查看某个函数的代码,以及该函数每行代码的指标信息,如果函数名不明确,会进行模糊匹配,比如list main会列出main.main和runtime.main。

traces
打印所有调用栈,以及调用栈的指标信息。

下面是具体的排查流程
1、使用top

$ go tool pprof http://0.0.0.0:6060/debug/pprof/goroutine
Fetching profile over HTTP from http://0.0.0.0:6060/debug/pprof/goroutine
Saved profile in C:\Users\Administrator\pprof\pprof.goroutine.006.pb.gz
Type: goroutine
Time: Jul 29, 2019 at 8:06am (CST)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Unrecognized command: "\x1b[A\x1b[Btop"
(pprof) top
Showing nodes accounting for 9, 100% of 9 total
Showing top 10 nodes out of 32
flat flat% sum% cum cum%
7 77.78% 77.78% 7 77.78% runtime.gopark
1 11.11% 88.89% 1 11.11% net/http.(*connReader).backgroundRead
1 11.11% 100% 1 11.11% runtime/pprof.writeRuntimeProfile
0 0% 100% 1 11.11% internal/poll.(*FD).Accept
0 0% 100% 1 11.11% internal/poll.(*FD).acceptOne
0 0% 100% 1 11.11% internal/poll.(*ioSrv).ExecIO
0 0% 100% 1 11.11% internal/poll.(*pollDesc).wait
0 0% 100% 1 11.11% internal/poll.runtime_pollWait
0 0% 100% 1 11.11% main.main
0 0% 100% 1 11.11% main.main.func1
(pprof)

我们可以看到有7个被阻塞了

我们通过traces打印出具体的调用链路

(pprof) traces
Type: goroutine
Time: Jul 29, 2019 at 8:06am (CST)
-----------+-------------------------------------------------------
5 runtime.gopark
runtime.goparkunlock
runtime.chansend
runtime.chansend1
main.main.func2
-----------+-------------------------------------------------------
1 runtime.gopark
runtime.netpollblock
internal/poll.runtime_pollWait
internal/poll.(*pollDesc).wait
internal/poll.(*ioSrv).ExecIO
internal/poll.(*FD).acceptOne
internal/poll.(*FD).Accept
net.(*netFD).accept
net.(*TCPListener).accept
net.(*TCPListener).AcceptTCP
net/http.tcpKeepAliveListener.Accept
net/http.(*Server).Serve
net/http.(*Server).ListenAndServe
net/http.ListenAndServe
main.main.func1
-----------+-------------------------------------------------------
1 runtime.gopark
runtime.goparkunlock
time.Sleep
main.main
runtime.main
-----------+-------------------------------------------------------
1 net/http.(*connReader).backgroundRead
-----------+-------------------------------------------------------
1 runtime/pprof.writeRuntimeProfile
runtime/pprof.writeGoroutine
runtime/pprof.(*Profile).WriteTo
net/http/pprof.handler.ServeHTTP
net/http/pprof.Index
net/http.HandlerFunc.ServeHTTP
net/http.(*ServeMux).ServeHTTP
net/http.serverHandler.ServeHTTP
net/http.(*conn).serve
-----------+-------------------------------------------------------
(pprof)

我们可以看到5个被阻塞到了 main.main.func2

然后我们可以使用list查看具体代码的阻塞

(pprof) list main.main.func2
Total: 9
ROUTINE ======================== main.main.func2 in D:\gowork\src\study\main\main.go
0 5 (flat, cum) 55.56% of Total
. . 29: }
. . 30: }()
. . 31: outCh := make(chan int)
. . 32: for i := 1; i <= 5; i++ {
. . 33: go func() {
. 5 34: outCh <- 1
. . 35: }()
. . 36: time.Sleep(time.Second)
. . 37: }
. . 38:
. . 39: ///value := <-outCh
(pprof)

我们可以看到已经将我们的代码阻塞块给打印出来了,我们就能很好的进行排查了。

参考:https://studygolang.com/articles/20529

参考:https://studygolang.com/articles/20519

参考:https://segmentfault.com/a/1190000019222661

go中内存泄露的发现与排查的更多相关文章

  1. java中内存泄露有几种?如何分析泄露原因

    一.Java内存回收机制 不论哪种语言的内存分配方式,都需要返回所分配内存的真实地址,也就是返回一个指针到内存块的首地址.Java中对象是采用new或者反射的方法创建的,这些对象的创建都是在堆(Hea ...

  2. Android中内存泄露与如何有效避免OOM总结

    一.关于OOM与内存泄露的概念 我们在Android开发过程中经常会遇到OOM的错误,这是因为我们在APP中没有考虑dalvik虚拟机内存消耗的问题. 1.什么是OOM OOM:即OutOfMemoe ...

  3. Qt中内存泄露和退出崩溃的问题 delete

    Qt中帮程序员做了一些内存回收的事情,但正因为这些反而让对此不熟悉的人会屡屡犯错. 收录一篇不错的文章: 在C++中学习过程中,我们都知道: delete 和 new 必须 配对使用(一 一对应):d ...

  4. Java中内存泄露及垃圾回收机制

    转自:http://blog.sina.com.cn/s/blog_538b279a0100098d.html 写的相当不错滴...................... 摘  要 Java语言中,内 ...

  5. C++中内存泄露的检测

    C++没有java的内存垃圾回收机制,在程序短的时候可能比较容易发现问题,在程序长的时候是否有什么检测的方法呢? 假设有一个函数可以某点检测程序的内存使用情况,那是否可以在程序开始的时候设置一个点,在 ...

  6. Windows系统中内存泄露与检测工具及方法

    1.检测需要使用的工具:windbg工具.检测前,需要先安装windbg工具.安装了该工具后,会在安装目录下有一个umdh工具.假设windbg安装在以下目录下:D:\Program Files\De ...

  7. java中内存泄露和内存溢出

    内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory:比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出. ...

  8. Qt中内存泄露和退出崩溃的问题

    http://blog.csdn.net/wangkuiyun/article/details/7412379

  9. 【微信小程序项目实践总结】30分钟从陌生到熟悉 web app 、native app、hybrid app比较 30分钟ES6从陌生到熟悉 【原创】浅谈内存泄露 HTML5 五子棋 - JS/Canvas 游戏 meta 详解,html5 meta 标签日常设置 C#中回滚TransactionScope的使用方法和原理

    [微信小程序项目实践总结]30分钟从陌生到熟悉 前言 我们之前对小程序做了基本学习: 1. 微信小程序开发07-列表页面怎么做 2. 微信小程序开发06-一个业务页面的完成 3. 微信小程序开发05- ...

随机推荐

  1. 使用Servlet和JSp在浏览器上实现对数据库表的增删改查(新手)

    第一步:用户输入网址进入一个登陆界面. 里面要有账号密码输入. 登陆界面链接到登陆的Servlet类中. Servlet类 --> 1.接收参数(账户密码)  2.调用DAO层的 SQL语句 验 ...

  2. WebRTC的RTCPeerConnection()原理探析

    从getUserMedia()到RTCPeerConnection(),自认为难度陡增.我想一方面是之前在Linux平台上学习ROS调用摄像头时,对底层的外设接口调用.摄像头参数都有学习理解:另一方面 ...

  3. 基于Ubuntu的ORB-SLAM2项目环境搭建过程

    目录 关于ORB-SLAM2 环境搭建 已有环境 创建环境 新建项目目录 安装Pangolin 安装OpenCV 3.2 安装Eigen DBoW2 and g2o (Included in Thir ...

  4. 在一台Linux服务器上安装多个MySQL实例(一)--使用mysqld_multi方式

    (一)MySQL多实例概述 实例是进程与内存的一个概述,所谓MySQL多实例,就是在服务器上启动多个相同的MySQL进程,运行在不同的端口(如3306,3307,3308),通过不同的端口对外提供服务 ...

  5. dvwa学习之七:SQL Injection

    1.Low级别 核心代码: <?php if( isset( $_REQUEST[ 'Submit' ] ) ) { // Get input $id = $_REQUEST[ 'id' ]; ...

  6. Codeforces Round #567 (Div. 2) B. Split a Number

    Split a Number time limit per test 2 seconds memory limit per test 512 megabytes input standard inpu ...

  7. 一个完整的机器学习项目在Python中的演练(一)

    大家往往会选择一本数据科学相关书籍或者完成一门在线课程来学习和掌握机器学习.但是,实际情况往往是,学完之后反而并不清楚这些技术怎样才能被用在实际的项目流程中.就像你的脑海中已经有了一块块"拼 ...

  8. Centos 8 安装 Consul-Template

    1. 下载安装包( consul-template_0.23.0_linux_amd64.zip 文件 ) 下载地址: https://releases.hashicorp.com/consul-te ...

  9. coding++:快速构建 kindeditor 富文本编辑器(一)

    此案例 demo 为 SpringBoot 开发 1.官网下载相关资源包:http://kindeditor.net/down.php 2.编写页面(引入相关JS) <!DOCTYPE html ...

  10. Thread -- Volatile

    1,可见性也就是说一旦某个线程修改了该被volatile修饰的变量,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,可以立即获取修改之后的值. 在Java中为了加快程序的运行效率,对一些变 ...