一. 项目结构

  • 在Go语言中web项目标准结构如下

--项目名--src--static--css--images--js--view--index.html--main.go

  • Go语言标准库中html/template包提供了html模版支持,把HTML当作模版可以在访问控制器时显示HTML模版信息

    • 这也符合标准的MVC思想

二.HTML模版显示

  • 使用template.ParseFiles()可以解析多个模版文件

// ParseFiles creates a new Template and parses the template definitions from
// the named files. The returned template's name will have the (base) name and
// (parsed) contents of the first file. There must be at least one file.
// If an error occurs, parsing stops and the returned *Template is nil.
//
// When parsing multiple files with the same name in different directories,
// the last one mentioned will be the one that results.
// For instance, ParseFiles("a/foo", "b/foo") stores "b/foo" as the template
// named "foo", while "a/foo" is unavailable.
func ParseFiles(filenames ...string) (*Template, error) {return parseFiles(nil, filenames...)
}
  • 把模版信息响应写入到输出流中

// Execute applies a parsed template to the specified data object,
// writing the output to wr.
// If an error occurs executing the template or writing its output,
// execution stops, but partial results may already have been written to
// the output writer.
// A template may be executed safely in parallel, although if parallel
// executions share a Writer the output may be interleaved.
func (t *Template) Execute(wr io.Writer, data interface{}) error {if err := t.escape(); err != nil {return err}return t.text.Execute(wr, data)
}
  • 代码演示,显示index.html信息

    • 因为配置的pattern为"/"所以资源路径任意,都可以访问到这个HTML

package main
​
import ("net/http""html/template"
)
​
func welcome(w http.ResponseWriter, r *http.Request) {t, _ := template.ParseFiles("view/index.html")t.Execute(w, nil) //第二个参数表示向模版传递的数据
}
​
func main() {server := http.Server{Addr: ":8090"}http.HandleFunc("/", welcome)server.ListenAndServe()
}

三.引用静态文件

  • 把静态文件放入到特定的文件夹中,使用Go语言的文件服务就可以进行加载

  • 项目结构

--项目--static--js--index.js--view--index.html--main.go
  • index.html代码如下

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd">
<html>
<head><title>Title</title><!--路径以斜杠开头,表示项目根目录下--><script type="text/javascript" src="/static/js/index.js"></script>
</head>
<body>这是要显示的html页面信息<br/><button onclick="myclick()">按钮</button>
</body>
</html>

index.js


function myclick(){alert("您点击了按钮")
}
  • 代码示例

package main
​
import ("net/http""html/template"
)
​
func welcome(w http.ResponseWriter, r *http.Request) {t, _ := template.ParseFiles("view/index.html")t.Execute(w, nil) //第二个参数表示向模版传递的数据
}
​
func main() {server := http.Server{Addr: ":8090"}/*访问url以/static/开头,就会把访问信息映射到指定的目录中*/http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))http.HandleFunc("/", welcome)server.ListenAndServe()
}

四. 向模版传递数据

  • 可以在HTML中使用{{}}获取template.Execute()第二个参数传递的值

  • 最常用的{{.}}中的"."是指针,指向当前变量,称为"dot"

  • 在{{}}可以有的Argument,官方给定如下

- go语法的布尔值、字符串、字符、整数、浮点数、虚数、复数,视为无类型字面常数,字符串不能跨行
- 关键字nil,代表一个go的无类型的nil值
- 字符'.'(句点,用时不加单引号),代表dot的值
- 变量名,以美元符号起始加上(可为空的)字母和数字构成的字符串,如:$piOver2和$;执行结果为变量的值,变量参见下面的介绍
- 结构体数据的字段名,以句点起始,如:.Field;执行结果为字段的值,支持链式调用:.Field1.Field2;字段也可以在变量上使用(包括链式调用):$x.Field1.Field2;
- 字典类型数据的键名;以句点起始,如:.Key;执行结果是该键在字典中对应的成员元素的值;键也可以和字段配合做链式调用,深度不限:.Field1.Key1.Field2.Key2;虽然键也必须是字母和数字构成的标识字符串,但不需要以大写字母起始;键也可以用于变量(包括链式调用):$x.key1.key2;
- 数据的无参数方法名,以句点为起始,如:.Method;执行结果为dot调用该方法的返回值,dot.Method();该方法必须有1到2个返回值,如果有2个则后一个必须是error接口类型;如果有2个返回值的方法返回的error非nil,模板执行会中断并返回给调用模板执行者该错误;方法可和字段、键配合做链式调用,深度不限:.Field1.Key1.Method1.Field2.Key2.Method2;方法也可以在变量上使用(包括链式调用):$x.Method1.Field;
- 无参数的函数名,如:fun;执行结果是调用该函数的返回值fun();对返回值的要求和方法一样;函数和函数名细节参见后面。
- 上面某一条的实例加上括弧(用于分组)执行结果可以访问其字段或者键对应的值:print (.F1 arg1) (.F2 arg2)(.StructValuedMethod "arg").Field
  • 向HTML传递字符串数据.在HTML中使用{{.}}获取传递数据即可.所有基本类型都是使用此方式进行传递

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd">
<html>
<head><title>Title</title>
</head>
<body>
<pre>
尊敬的{{.}}先生/女士您已经被我公司录取,收到此消息后请您仔细阅读附件中"注意事项"再次祝您:{{.}}好运
</pre>
</body>
</html>
package main
​
import ("net/http""html/template"
)
​
func welcome(w http.ResponseWriter, r *http.Request) {t, _ := template.ParseFiles("view/index.html")t.Execute(w, "smallming") //此处传递数据
}
​
func main() {server := http.Server{Addr: ":8090"}http.HandleFunc("/", welcome)server.ListenAndServe()
}

传递结构体类型数据

  • 结构体的属性首字母必须大写才能被模版访问

  • 在模版中直接使用{{.属性名}}获取结构体的属性

  • HTML代码如下

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd">
<html>
<head><title>Title</title>
</head>
<body>
<pre>
当前登录用户信息:<br/>姓名:{{.Name}}<br/>年龄:{{.Age}}
</pre>
</body>
</html>
  • go文件代码如下

  • User.go

  • package entity
    //注意:只有首字母大写的属性才能在模版中访问到
    type  User struct {Name stringAge int
    }
    
package mainimport ("demo/entity""html/template""net/http"
)
/*HTML模板和静态资源
*/
func welcome(w http.ResponseWriter, r *http.Request) {t, _ := template.ParseFiles("view/index.html")user:=new(entity.User)user.Name="smallming"user.Age=18//t.Execute(w, entity.User{Name:"小明",Age:18}) //第二个参数表示向模版传递的数据t.Execute(w,user)
}func main() {server := http.Server{Addr: "192.168.12.37:8090"}/*访问url以/static/开头,就会把访问信息映射到指定的目录中*/http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))http.HandleFunc("/", welcome)server.ListenAndServe()
}

向模版传递map类型数据

  • 直接使用{{.key}}获取map中数据

  • 模版中支持连缀写法(不仅仅是map)

  • go文件代码如下

package main
​
import ("net/http""html/template"
)
​
//注意:只有首字母大写的属性才能在模版中访问到
type User struct {Name stringAge  int
}
​
func welcome(w http.ResponseWriter, r *http.Request) {t, _ := template.ParseFiles("view/index.html")m := make(map[string]interface{})m["user"] = User{"张三", 20}m["money"] = 998t.Execute(w, m) //此处传递数据
}
​
func main() {server := http.Server{Addr: ":8090"}http.HandleFunc("/", welcome)server.ListenAndServe()
}
  • HTML代码如下,里面使用了连缀写法

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd">
<html>
<head><title>Title</title>
</head>
<body>
<pre>
当前登录用户信息:<br/>姓名:{{.user.Name}}<br/>年龄:{{.user.Age}}<br/>购物金额:{{.money}}
</pre>
</body>
</html>

调用方法

  • 在模版中调用函数时,如果是无参函数直接调用函数名即可,没有函数的括号

  • 例如在go源码中时间变量.Year()在模版中{{时间.Year}}

  • 在模版中调用有参函数时参数和函数名称之间有空格,参数和参数之间也是空格

  • 给定go文件代码

package main
​
import ("net/http""html/template""time"
)
​
​
func welcome(w http.ResponseWriter, r *http.Request) {t, _ := template.ParseFiles("view/index.html")time:=time.Date(2018,1,2,3,4,5,0,time.Local)t.Execute(w, time) //此处传递数据
}
​
func main() {server := http.Server{Addr: ":8090"}http.HandleFunc("/", welcome)server.ListenAndServe()
}
  • html代码如下

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd">
<html>
<head><title>Title</title>
</head>
<body>
<pre><!--调用time变量的无参方法-->取出时间中的年:{{.Year}} <br/>取出时间中的年:{{.Month}} <br/><!--调用有参数方法-->格式化后的内容:{{.Format "2006-01-02"}}
</pre>
</body>
</html>

调用自定义函数/方法

  • 如果希望调用自定义函数,需要借助html/template包下的FuncMap进行映射

  • FuncMap本质就是map的别名type FuncMap map[string]interface{}

  • 函数被添加映射后,只能通过函数在FuncMap中的key调用函数

  • go文件代码示例

package main
​
import ("net/http""html/template""time"
)
​
//把传递过来的字符串时间添加一分钟后返回字符串格式时间
func MyFormat(s string) string{t,_:=time.Parse("2006-01-02 15:04:05",s)t=t.Add(60e9)//在时间上添加1分钟return t.Format("2006-01-02 15:04:05")
}
​
func html(res http.ResponseWriter, req *http.Request) {//把自定义函数绑定到FuncMap上funcMap:=template.FuncMap{"mf":MyFormat}//此处注意,一定要先绑定函数t:=template.New("demo.html").Funcs(funcMap)//绑定函数后在解析模版t, _ = t.ParseFiles("demo.html")s:="2009-08-07 01:02:03"t.Execute(res, s)
}
func main() {server := http.Server{Addr: "localhost:8090",}http.HandleFunc("/html", html)server.ListenAndServe()
}
  • HTML代码示例

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>传递过来的时间:{{.}}<br/>调用自定义函数,格式化后的时间:{{mf .}}<br/>
</body>
</html>

五. Action

  • Go语言官方文档给出action(动作)的列表。"Arguments"和"pipelines"代表数据的执行结果

{{/* a comment */}}注释,执行时会忽略。可以多行。注释不能嵌套,并且必须紧贴分界符始止,就像这里表示的一样。
{{pipeline}}pipeline的值的默认文本表示会被拷贝到输出里。
{{if pipeline}} T1 {{end}}如果pipeline的值为empty,不产生输出,否则输出T1执行结果。不改变dot的值。Empty值包括false、0、任意nil指针或者nil接口,任意长度为0的数组、切片、字典。
{{if pipeline}} T1 {{else}} T0 {{end}}如果pipeline的值为empty,输出T0执行结果,否则输出T1执行结果。不改变dot的值。
{{if pipeline}} T1 {{else if pipeline}} T0 {{end}}用于简化if-else链条,else action可以直接包含另一个if;等价于:{{if pipeline}} T1 {{else}}{{if pipeline}} T0 {{end}}{{end}}
{{range pipeline}} T1 {{end}}pipeline的值必须是数组、切片、字典或者通道。如果pipeline的值其长度为0,不会有任何输出;否则dot依次设为数组、切片、字典或者通道的每一个成员元素并执行T1;如果pipeline的值为字典,且键可排序的基本类型,元素也会按键的顺序排序。
{{range pipeline}} T1 {{else}} T0 {{end}}pipeline的值必须是数组、切片、字典或者通道。如果pipeline的值其长度为0,不改变dot的值并执行T0;否则会修改dot并执行T1。
{{template "name"}}执行名为name的模板,提供给模板的参数为nil,如模板不存在输出为""
{{template "name" pipeline}}执行名为name的模板,提供给模板的参数为pipeline的值。
{{with pipeline}} T1 {{end}}如果pipeline为empty不产生输出,否则将dot设为pipeline的值并执行T1。不修改外面的dot。
{{with pipeline}} T1 {{else}} T0 {{end}}如果pipeline为empty,不改变dot并执行T0,否则dot设为pipeline的值并执行T1。
  • action主要完成流程控制、循环、模版等操作.通过使用action可以在模版中完成简单逻辑处理(复杂逻辑处理应该在go中实现,传递给模版的数据应该是已经加工完的数据)

if 使用

  • if写在模版中和写在go文件中功能是相同的,区别是语法

  • 布尔函数会将任何类型的零值视为假,其余视为真。

  • if后面的表达式中如果包含逻辑控制符在模版中实际上是全局函数

and函数返回它的第一个empty参数或者最后一个参数;就是说"and x y"等价于"if x then y else x";所有参数都会执行;
or返回第一个非empty参数或者最后一个参数;亦即"or x y"等价于"if x then x else y";所有参数都会执行;
not返回它的单个参数的布尔值的否定
len返回它的参数的整数类型长度
index执行结果为第一个参数以剩下的参数为索引/键指向的值;如"index x 1 2 3"返回x[1][2][3]的值;每个被索引的主体必须是数组、切片或者字典。
print即fmt.Sprint
printf即fmt.Sprintf
println即fmt.Sprintln
html返回其参数文本表示的HTML逸码等价表示。
urlquery返回其参数文本表示的可嵌入URL查询的逸码等价表示。
js返回其参数文本表示的JavaScript逸码等价表示。
call执行结果是调用第一个参数的返回值,该参数必须是函数类型,其余参数作为调用该函数的参数;如"call .X.Y 1 2"等价于go语言里的dot.X.Y(1, 2);其中Y是函数类型的字段或者字典的值,或者其他类似情况;call的第一个参数的执行结果必须是函数类型的值(和预定义函数如print明显不同);该函数类型值必须有1到2个返回值,如果有2个则后一个必须是error接口类型;如果有2个返回值的方法返回的error非nil,模板执行会中断并返回给调用模板执行者该错误;
  • 二元比较运算的集合:(也是函数,函数具有两个参数,满足参数语法)

eq      如果arg1 == arg2则返回真
ne      如果arg1 != arg2则返回真
lt      如果arg1 < arg2则返回真
le      如果arg1 <= arg2则返回真
gt      如果arg1 > arg2则返回真
ge      如果arg1 >= arg2则返回真
  • 简单if示例-go文件

package main
​
import ("net/http""html/template"
)
​
func test(rw http.ResponseWriter, r *http.Request) {t, _ := template.ParseFiles("template/html/if.html")//第二个参数传递类型默认值:nil,"",0,false等都会导致if不成立t.Execute(rw, "")
}
​
func main() {//创建server服务server := http.Server{Addr: ":8090"}
​//设置处理器函数http.HandleFunc("/test", test)
​//监听和开始服务server.ListenAndServe()
}
  • 简单if示例-html文件

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>if测试</title>
</head>
<body>
测试if是否执行<br/>
{{if . }}
if成立这个位置输出
{{end}}
</body>
</html>
  • 直接在HTMl中定义变量演示if..else用法(go文件不变)

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>测试</title>
</head>
<body>
{{$n:=123}}
{{if ne $n 123}}
if成立这个位置输出
{{else}}{{/* 比if结构多了else */}}
这是else的功能
{{end}}
</body>
</html>
  • go文件不变,演示if...else if...else用法

<body>
{{$n:=124}}
{{if eq $n 123}}
123
{{else if eq $n 124}}
124
{{else if eq $n 125}}
125
{{else}}
else
{{end}}
</body>
  • 在模版中也可以相互嵌套

<body>
{{$n:=124}}
{{if gt $n 100}}{{if gt $n 200}}gt 200{{else}}lt 200{{end}}
{{else}}小于100
{{end}}
</body>

range使用

  • range遍历数组或切片或map或channel时,在range内容中{{.}}表示获取迭代变量


<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<script type="text/javascript" src="/static/js/myclick.js">
</script>
<body>
获取map数据<br/>{{range .}}{{.}}{{/* 此处dot为迭代变量 */}}<br/>{{end}}
</body>
</html>

后台传值

package mainimport ("demo/entity""html/template""net/http"
)
func welcome(w http.ResponseWriter, r *http.Request) {t, _ := template.ParseFiles("view/index.html")user:=new(entity.User)user.Name="smallming"user.Age=18strings := make(map[string]string);strings["1"]="我是1"strings["2"]="我是2"strings["3"]="我是3"//t.Execute(w, entity.User{Name:"小明",Age:18}) //第二个参数表示向模版传递的数据t.Execute(w,strings)
}func main() {server := http.Server{Addr: "192.168.12.37:8090"}/*访问url以/static/开头,就会把访问信息映射到指定的目录中*/http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))http.HandleFunc("/", welcome)server.ListenAndServe()
}

页面显示

模版嵌套

  • 在实际项目中经常出现页面复用的情况,例如:整个网站的头部信息和底部信息复用

  • 可以使用动作{{template "模版名称"}}引用模版

  • 引用的模版必须在HTML中定义这个模版

{{define "名称"}}
html
{{end}}
  • 执行主模版时也要给主模版一个名称,执行时调用的是ExecuteTemplate()方法

{{define "layout"}}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd">
<html>
<head><title>Title</title>
</head>
<body>
{{template "head" }}<br/>
中间的内容<br/>
{{template "foot" }}
</body>
</html>
{{end}}
{{define "head"}}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd">
<html>
<head><title>Title</title>
</head>
<body>
head.html
</body>
</html>
{{end}}
{{define "foot"}}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd">
<html>
<head><title>Title</title>
</head>
<body>foot.html
</body>
</html>
{{end}}
package main
​
import ("net/http""html/template"
)
​
func welcome(w http.ResponseWriter, r *http.Request) {//要加载所有需要被嵌套的文件t, _ := template.ParseFiles("view/index.html", "view/head.html", "view/foot.html")//执行主模版,主要调用的方法t.ExecuteTemplate(w, "layout", nil)
}
​
func main() {server := http.Server{Addr: ":8090"}http.HandleFunc("/", welcome)server.ListenAndServe()
}

调用模版时同时传递参数

  • 如果直接引用html可以直接使用html标签的<iframe>,但是要动态效果时,可以在调用模版给模版传递参数

{{define "layout"}}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd">
<html>
<head><title>Title</title>
</head>
<body>
{{template "head" "head的参数"}}<br/>
中间的内容<br/>
{{template "foot" "foot的参数"}}
</body>
</html>
{{end}}
  • 在子模版中依然是使用{{.}}获取传递过来的参数

{{define "head"}}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd">
<html>
<head><title>Title</title>
</head>
<body>
head.html
{{.}}
</body>
</html>
{{end}}

Golang-Web(HTMl模板和静态资源)相关推荐

  1. Golang | Web开发之Gin静态资源映射及HTML模板渲染

    欢迎关注「全栈工程师修炼指南」公众号 点击

  2. Django知识点:模板和静态资源文件

    模板 1. 模板介绍 web开发:给用户提供一个可视化页面,包含两部分,django 模板系统能够完成这两部分的内容 静态页面:css,html,js,img 动态数据:需要使用模板语法将数据渲染 2 ...

  3. apache整合tomcat实现web服务器的动静态资源的分离解析

    前段时间因为一点关系,接触到apache动态进负载均衡的实验,很自然想到apache配合其他软件使用完成需求,网上找了下,还是比较多的apache整合tomcat的.所以我就将做到这个实验从头到尾思路 ...

  4. web中什么是静态资源和动态资源

    静态资源和动态资源的概念: 静态资源:我的理解是前端的固定页面,这里面包含HTML.CSS.JS.图片等等,不需要查数据库也不需要程序处理,直接就能够显示的页面. 具体形式为:客户端发送请求到web服 ...

  5. tomcat idea项目访问、下载web项目外的静态资源(图片、文件),js下载项目外的文件,server.xml设置对idea中的项目不生效

    读取项目外的文件,配置Tomcta的虚拟路径,  打开文件:Tomcat/conf/server.xml  修改文件配置: <Host name="localhost" ap ...

  6. golang web 静态文件

    目录 概述 原始方式 http.FileServer http.ServeContent 使用场景 总结 概述 在 Web 开发中,需要处理很多静态资源文件,如 css/js 和图片文件等.本文将介绍 ...

  7. SpringBoot整合Thymeleaf模板引擎以及静态资源的访问

    SpringBoot整合Thymeleaf模板引擎静态资源访问的配置 Thymeleaf是一个现代服务器端Java模板引擎,适用于Web和独立环境,能够处理HTML,XML,JavaScript,CS ...

  8. SpringMVC在web.xml中配置DispatcherServlet拦截了静态资源访问

    如图 在web.xml中配置DispatcherServlet时对于url-pattern的配置方式有以下几种情况: 1.配置为: *.do 或者是 *.action 时,拦截以.do或者.actio ...

  9. 构建多页面应用——静态资源的处理

    在之前的系列文章中,我已经介绍了如何用webpack实现多页面应用的js,html,css的处理.今天就主要介绍如何处理静态资源,在web开发中最常见的静态资源就是图片. 图片的引用方式 而因为在we ...

最新文章

  1. 提高C++性能的编程技术笔记:引用计数+测试代码
  2. python3写unicode编码到文件
  3. Vue 组件中 移动 this.$el 的注意事项
  4. 自由自在带你品尝一种能长出果蔬的冰淇淋
  5. UOJ 405(IOI2018 D1T1)
  6. springboot 参数校验详解
  7. python三大器_Python - 三大器 迭代器,生层器,装饰器
  8. 模拟实现priority_queue优先级队列
  9. 小米集团本周再回购1920万港元股票
  10. linux下ftp二进制传输,FTP的两种传输模式:BINARY和ASCII
  11. 抢红包算法 c++_【优化求解】基于粒子群算法的光伏电池MPPT控制策略
  12. linux 下安装小度wifi,Fedora/Ubuntu如何安装小度/360随身WIFI驱动
  13. 交通行业如何做好数字化转型?| 推荐收藏
  14. 六自由度机械手正逆运动学
  15. 【第一节】抠图 -- 薄、透、露的朦胧感
  16. Cocos2d-x学习笔记(五)仿真树叶飘落效果的实现(精灵旋转、翻转、钟摆运动等综合运用)
  17. js打印去除页眉页脚
  18. Gluster部署案例
  19. Python 语言发展历史
  20. Dell服务器型号的详解

热门文章

  1. 删除Symbian模拟器(功能表-安装)里面测试程序的方法
  2. 蝉知自定义HTML,如何安装蝉知主题
  3. 邮件api接口免费试用
  4. Linux的rsa命令,openssl命令行进行RSA加密解密
  5. FreeModbus应用系列之一
  6. 三个国家贸易,单据出口方可以是中间国家吗?
  7. 这四款PC软件能够帮你在工作中轻松脱颖而出
  8. 关于目标与执行力的思考
  9. Rhino 6 偏移曲面 选项有很多玄机
  10. Java内存之本地内存分析神器: NMT 和 pmap