Vim字符编码及中文菜单乱码

vim经常遇到文件乱码的情况,很多的文章都只是解决了作者遇到的乱码问题,不同的使用者由于环境不一样,参考之后,反而更加混淆和复杂。

其实vim乱码是与系统环境非常相关的,一味执着于修改vim的配置而不知道分析系统的实际环境,往往导致混淆,本文从原理分析vim编码的设计和乱码原因,帮助所有的用户解决vim的乱码。

vim为何会出现乱码:

  1. 首先是输入,vim 以错误的格式解析文件,比如原本是utf-8,但以ansi解析,那必然是乱码了。
  2. 然后是处理,vim 以错误的格式处理文件,比如原本是utf-8,但内部以 ansi 保存处理,导致乱码。
  3. 然后是输出,vim 输出显示的编码与系统不一致,也会乱码。
  4. 最后是写入,vim 回写文件与打开的不一致,造成编辑后文件乱码。

在 Linux 下需要考虑:

  1. Linux 默认支持的语系,/etc/sysconfig/i18n
  2. 终端 bash 的语系,与 $LANG 变量有关
  3. 再就是上面提到的文件编码有关

VIM编码相关选项

Vim 有四个跟字符编码方式有关的选项,encodingfileencodingfileencodingstermencoding (这些选项可能的取值请参考 Vim 在线帮助 :help encoding-names),它们的意义如下:

encoding

encoding(enc) : Vim 内部使用的字符编码方式,包括 Vim 的 buffer (缓冲区)、寄存器中字符串、菜单文本、消息文本等。用户手册上建议只在 .vimrc 中改变它的值,事实上似乎也只有在 .vimrc 中改变它的值才有意义。Vim 读取文件之后,但并不以读取文件的编码来处理,而是会转换成内部编码的格式。这个编码默认是系统 locale 决定,一般与操作系统相关,linux下utf-8居多,中文windows下则是gbk。可以使用 :set enc 来查看当前 enc 设置

fileencoding

fileencoding(fenc) : 用于配置打开文件和保存文件的编码,fenc 是当前缓冲区文件自身的编码。从磁盘读取文件, Vim 会对文件编码检查,如果文件的编码与 Vim 内部编码(enc) 不同, Vim 会对文本做转码。当 Vim 向磁盘写文件时, enc 与 fenc 不一样, Vim 会转码成 fenc 编码保存文件。

该设置只能有一个值,只适合少数文件都是同种编码的环境

fileencodings

fileencodings(fencs): 这是一个字符编码的列表,Vim 启动时会按照它所列出的字符编码方式逐一探测即将打开的文件的字符编码方式,并且将 fileencoding 设置为最终探测到的字符编码方式。因此最好将 Unicode 编码方式放到这个列表的最前面,将拉丁语系编码方式 latin1 放到最后面。从名字上看就知道是fileencoding的增强版,可以配置多种不同的编码,常见的配置为,配置好之后,列表中的文本编码只要合法,都能被 Vim 正确的读取。

termencoding

termencoding(tenc): Vim 所工作的终端 (或者 Windows 的 Console 窗口) 的字符编码方式,或者说是 Vim 用于屏幕显示时的编码。这个选项在 Windows 下对我们常用的 GUI 模式的 gVim 无效,而对 Console 模式的 Vim 而言就是 Windows 控制台的代码页,并且通常我们不需要改变它。Vim 输出的编码,输出指输出到操作系统或命令终端等,默认与操作系统的语言编码一致,如果使用linux命令终端,建议终端和linux系统配置相同的编码,然后配置相同的termencoding,否则顾全了vim就顾不上shell,不过如果shell不存在中文名文件,则配置终端和termencoding一致即可,对于windows,能自动的识别gbk和utf-8,不用特殊配置。

fileformats

fileformats,用于区分操作系统,主要是回车\r\n的区别,fileformats选项,用于处理文件格式问题。以下命令,告诉 Vim 将UNIX文件格式做为第一选择,而将MS-DOS的文件格式做为第二选择:(换行方式 在早期的打印机时代,开始新的一行要占用两个字符的时间。如果到了一行的结尾处,你要快速回到新的一行的开头,需要打印针头在纸面上飞快地掠过,常常会在纸面上留下污点。解决这个问题的办法就是,用两个字符:一个字符来移到第一列,另一个字符来新增一行。计算机产生以后,存储较为昂贵,在如何解决回车换行这个老问题上,人们产生了不同的意见。UNIX人认为在到达一行的结尾时新增一行 (LF),而Mac人则认同 (CR)的解决办法,MS则坚持古老的 (CRLF)的方法。这就意味着如果你将一个文件从一个系统转移到另一个系统,就面临着回车换行的问题。而 Vim 编辑器则会自动的认出这种文件格式方面的区别,并做出相应处理。)

set fileformats=unix,dos

VIM 多字符编码方式工作流程

再记录一下,Vim 的多字符编码方式支持是如何工作的:

  1. Vim 启动,根据 .vimrc 中设置的 encoding 的值来设置 buffer、菜单文本、消息文的字符编码方式。
  2. 读取需要编辑的文件,根据 fileencodings 中列出的字符编码方式逐一探测该文件编码方式。并设置 fileencoding 为探测到看起来是正确的字符编码方式,如果没有找到合适的编码,就用latin-1(ASCII)编码打开。
  3. 对比 fileencoding 和 encoding 的值,若不同则调用 iconv 将文件内容转换为 encoding 所描述的字符编码方式,并且把转换后的内容放到为此文件开辟的 buffer 里,此时我们就可以开始编辑这个文件了。
  4. 编辑完成后保存文件时,再次对比 fileencoding 和 encoding 的值。若不同,再次调用 iconv 将即将保存的 buffer 中的文本转换为 fileencoding 所描述的字符编码方式,并保存到指定的文件中。

由于 Unicode 能够包含几乎所有的语言的字符,而且 Unicode 的 UTF-8 编码方式又是非常具有性价比的编码方式 (空间消耗比 UCS-2 小),因此建议 encoding 的值设置为 utf-8。这么做的另一个理由是 encoding 设置为 utf-8 时,Vim 自动探测文件的编码方式会更准确 (或许这个理由才是主要的 ;) 。我们在中文 Windows 里编辑的文件,为了兼顾与其他软件的兼容性,文件编码还是设置为 GB2312/GBK 比较合适,因此 fileencoding 建议设置为 chinese (chinese 是个别名,在 Unix 里表示 gb2312,在 Windows 里表示 cp936,也就是 GBK 的代码页)。

vimrc中设置

在分析了乱码原因,了解了vim编码的参数之后,就可以根据实际情况来配置了

  1. 分析文件编码,配置 Vim 文件文件解析编码fileencodings,让 Vim 能解析出来
  2. 分析系统编码,配置 Vim 内码encoding,如果linux系统语言为ansi,则必须配置内码,否则 Vim 无法处理中文,中文windows下 Vim 内码为gbk,但还是建议统一配置为utf-8
  3. 根据输出配置显示编码,linux系统如果使用了putty或者SecureCRT,需要注意配置termencoding和终端软件一致,windows系统比较少有终端软件,系统能自动解析gdb和utf-8,建议统一配置为utf-8

参考的 Vim 编码配置如下,linux 和 windows 配置相同,linux系统语言编码和ssh终端编码配置为utf-8,windows则不需要配置,即可正常的打开utf-8,gdk等编码的文件

" Vim 内部编码
set encoding=utf-8
"按照utf-8 without bom,utf-8,顺序识别打开文件
set fileencodings=ucs-bom,utf-8,gbk,gb2312,cp936,big5,gb18030,shift-jis,euc-jp,euc-kr,latin1

set fileencoding=utf-8

"防止菜单乱码
if(has("win32") || has("win64") || has("win95") || has("win16"))
	source $VIMRUNTIME/delmenu.vim
	source $VIMRUNTIME/menu.vim
	language messages zh_CN.utf-8
endif
"默认以双字节处理那些特殊字符
if v:lang =~? '^\(zh\)\|\(ja\)\|\(ko\)'
	set ambiwidth=double
endif

set nobomb "不自动设置字节序标记

参考:


2015-05-01 vim , linux , encode , fileencoding

开始使用 Vim 的方式

这是不是一篇 Vim 的教程,只是一个入门 Vim 的流程,如果你从未听说过 Vim,但是希望学习 Vim,那么可以继续往下看,如果想要了解 Vim 更多的功能可以参考博客中其他关于 Vim 的文章。

Brief Introduction

Vim 作为一款 Linux 下必备的编辑器,如果学会了使用,非常强大,但是不得不说 Vim 的入门实在是太困难了,他的学习曲线也会经过非常长的一段平缓期。当然如果只是简单的使用插入功能,i,进入插入模式,直接敲,敲完 esc 退出,再:wq保存退出,也就是最基本的入门了。当时如果想要尝试其他的功能,就不得不看文档,装插件,装配色方案,写 Vimrc 配置等等了。

其实很多 Vimer 都会对新手说一句看帮助文档,这句话对新手真的毫无用处,或许真的只有对 Vim 有了一定了解才能在文档的帮助下学到 Vim 更多的东西。文档一般都是大而全,有很多部分其实完全没有必要再新手阶段的时候就全部读完,而甚至有些命令,设置用了多年的 Vimer 都不会去接触一下。所以如何入门?首先要了解作为一个编辑器最基本的功能,写入,删除,修改,保存,编码,而对于 Vim,还有几大模式,移动光标,替换操作等等。

Vimtutor

在最短的时间内了解这个编辑器的基本功能,至少把他能够当成一个编辑器使,再不济能够直接使用键盘来浏览文件内容,对文件内容进行修改,删除,保存。当然在几十分钟内你可能要记住几十个命令,以及每个命令对应的操作。可能这对于很多新手来说需要锻炼一段时间才能记住,所以不要怕,用记事本,或者 Vim 记下来每个命令和对应的功能,之后不断的练习,很短的时间就能习惯这种操作。在习惯了之后就会发现再移动鼠标是多么的慢。当然你可能在 Vim 默认配置中有很多不太习惯的地方,把你那些不习惯记住,把那些你想要的功能一个一个记下来,在以后的学习中,我们来一一解决。

比如我在最初的时候觉得 j/k 在行之间移动,有些时候中文一行比较长,显示的时候跳跃了很多行,而是用 j/k 移动光标的时候在几行之间跳跃,让我很不习惯,当然这个在之后的学习中偶然看到了原来只要在 VimRC 中配置<map> j gj就能够用 j 来直接到下一行,即使这一行事实上是 Vim 中的一行。还有很多很多的问题,都在短短几天的时间内一一得到了解决。所以开始的时候用极短的时间略读一遍 Vimtutor 先熟悉。哦,对了,Vimtutor 在哪里,Windows 下是在 Vim 安装目录下Vimtutor.bat这个文件,双击运行,而 Linux 下直接终端运行 Vimtutor 即可。当然这一切都在 Vim 的帮助文档中写着了,你可以尝试在 Vim 普通模式下运行:help VimtutorVim 会把文档全部显示出来。

Vimrc

在熟悉了最基本的命令之后,你可以开始看文档,尝试修改 Vimrc 配置,当然如果你不想看英文文档,Vim 还是有中文文档 的。不想覆盖:help 的文档,直接下一个 PDF 的随时查看也挺好,在熟悉了一下命令之后,直接看英文的也就很快了。我的方法就是直接看别人的配置,去 Github 上面一搜,很多人把自己的配置都公开了,就去一条条试试那些命令,有什么作用,我自己能怎么修改,然后查文档,很快你自己熟悉的配置就有了。

Plugin

当然在熟悉 VimRC 配置之后,在随便哪个文件中只要运行:edit $MYVimRC就能够直接编辑 Vimrc 文件,我在网上看别人的博客,于是我也就用了很多人推荐的 Vundle 来管理我的插件,从此之后再不用下载,解压,复制,只需要修改 Vimrc 配置就能够安装插件,同样也只要保存一份配置文件就能够同步我的插件,这件事简直太棒了。当然最初安装 Vundle 这件事情是要你自己来的。直接去 Vundle 主页看吧。

colorscheme

或许在练习了 Vim 不久就会觉得 Vim 的颜色太单调,但是没关系,Vim 有很多很多的配色方案,只要你能找到总能找到自己喜欢的,我直接用了别人推荐的 molokai, 你也可以试试,下载 molokai.Vim 文件到 colors 目录下之后,在 Vimrc 中写入colorscheme molokai,就能使用这款配色了。当然你也能自己定义字体和字号,还有很多你能自己定义的东西等待发掘的。

附送两张图

vi tutorial

vi key command


2015-05-01 vim , tutorial , linux , editor

cloudup 尝鲜

应该是很久以前在听到一个韩国FM节目的时候遇到得这个网盘 Cloudup,当时直接能在网页上播放音频视频,所以觉得还不错,可是当我想去注册时才发现这个网站需要邀请才能注册,没办法只能申请邀请,于是等了很久很久之后,今天早上打开邮箱,官方终于邀请我去注册了。

官方的邮件也极其简单:

Thanks a million for joining the Cloudup wait list. You and your pals can now signup without waiting a minute longer!

Create your account now to start sharing files, links, pictures, gifs, videos, and documents instantly and beautifully with anyone. Just drag, drop, & stream.

Let me know if you have any questions, and have a wonderful week!

虽然简单但也把问题说的清清楚楚,这个社会已经没人不知道网盘服务了,所以也就么有必要解释,简单的说“能够分享文件,链接,图片,gifs,视频和文档”这也就够了。再把上传过程“drag,drop&stream”,分享界面beautiful说明白,真的足够了。其实我最关心的stream,很少网盘能够支持音视频直接串流的。

注册了账号体验了一把,界面清爽,操作简单,分享的链接漂亮,预览快速,支持直链,给我用也足够了。哦对了,这个网盘用item的数量来表示空间,至多能上传1000份文件,每个文件至多200M。我想也足够了呢。

你可以尝试我分享的Music:https://cloudup.com/cuP0Yl1BeGj

还有文档:https://cloudup.com/cSvQXovNXBx

最后你如果也想试试,我的邀请:https://cloudup.com/s/einverne-865

161011 更新

今天心血来潮,用 Cloudup 做个 Podcast 倒是很好的选择,查了这么久的 Podcast Hosting,各种收费,VPS,可是这个现成的 Cloudup 不是刚刚好么,1000个文件,想想都无法做到那么多,然后稳定性,经过这么多时间的观察,似乎这个已经是可以认可的了,并且这个云盘服务没有大规模扩张,一直保持着清爽的界面,真不知道后面是如何赚钱的,希望不会倒闭吧。


2015-04-30 products , cloudup , 网盘

每天学习一个命令:inxi 显示系统和硬件信息

inxi 是一个用来显示系统基本信息的脚本,输出结果经过整理适合打印到控制台或者 IRC,也非常适合在论坛中寻求技术支持时将结果贴出来。inxi 的输出信息包括系统硬件,CPU,驱动,Xorg,桌面环境,Kernel,GCC 版本,Processes,内存使用量,和其他一些信息。结果的输出颜色可以使用 -c 0 来禁用。

为了尽可能的保持基本的隐私和安全,inxi 自动过滤了一些敏感信息,包括网卡 mac 地址,WAN 和 LAN IP 地址,桌面用户名和其他一些内容。

安装

sudo apt-get install inxi

参数说明

-A          显示音频 声卡相关信息
-b          输出基本信息
-c [0-32]   不同的配色
-C          显示完整的 CPU 信息
-d          显示光驱和硬盘相关信息
-D          显示硬盘相关信息
-F          完整报告
-G          显卡
-i          WAN IP
-I          processes, uptime, memory, shell type etc
-m          RAM data
-M          machine data 主板,BIOS etc
-p          完整分区信息
-S          系统信息,hostname,kernel,桌面环境,发行版等等
-u          分区 UUID

使用实例

查看电脑硬件型号

inxi -F

监控 CPU 进程和内存使用

inxi -t c
inxi -t m
inxi -t cm

查看 CPU 温度和风扇速度

inxi -s

2015-03-28 linux , command

在线协同编程网站收集

在线同步的代码编辑工具,可以用于实时分享代码,也可以用于 interview,

code.meideng

一个很简洁但是很高效的网站

  • https://code.meideng.net/

codeadvice

codeadvice

  • https://www.codeadvice.io/

2015-03-27 online , website , coding

魅蓝Note折腾记

##魅蓝Note的一些设置和想法

  1. 账户–点击账户名–系统权限可以开启ROOT,默认是关闭的,开启后不再享受三包!
  2. 安全–权限与凭证–允许安装位置来源~-开启这个,否则一些软件将无法正常安装。默认是关闭的。
  3. 辅助功能–有很多实用功能
    • 手势唤醒里常用设置可以参考:双击=点亮屏幕,上滑=解锁,下滑=打开通知面板,V=手电
    • 长按主键可以设置关闭屏幕或语音助手
    • 实时网速,依个人需求开启
    • 智能隐藏Smartbar 推荐开启
  4. 关于手机–存储–清除手机数据=恢复出厂设置。建议:恢复出厂之前记得备份!手机账户-云存储-可以备份联系人及桌面布局
  5. 一些补充:
    • 如果手机出现黑屏,无法点亮的问题不要着急。长按电源键7S可重启。然后官网下载最新版固件升级即可。
    • 对于App的缺点,则不在我的考虑范围,不喜欢拨号盘,那我可以更换其他App,不喜欢短信app,我也可以换,这就是Android的优势。所以我替换了桌面TSF Launcher,浏览器Chrome,短信Messenger,地图Google Map,音乐Play Music & NetEase Music,记事本Keep,日历Google Calendar,搜索Google Search······我是不是有点强迫症了。
    • 看到貌似是和LBE合作了,有一个简单的安全中心,将就用吧,反正之前也是用LBE
    • 没有纯正Android的返回,HOME,最近应用的三个虚拟键,很不喜欢,略奇怪的操作

##魅蓝Note如何安装GApps

  1. Root 账户–点击账户名–系统权限可以开启ROOT,默认是关闭的,开启后不再享受三包!
  2. 两个地址下载FQ工具
  3. Google安装器
  4. 登录FQ,输入SS帐号,登录Google Play Store,更新各项服务

参考:


2015-03-13 Android , Root , Meizu

返乡

南都讯 春节期间,一个上海大学博士生的返乡笔记《一位博士生的返乡笔记:近年情更怯,春节回家看什么》在微信朋友圈及微博等社交媒体疯传,截至昨晚9点,该文在微信公众号“市政厅”上阅读量已超过4.6万多次。近似“疯狂”的转发量让作者王磊光感到不安和不理解,在他看来这只是一个媒体事件,自己不过是“不小心卷入了其中”。

王磊光

作者否认美化绿皮火车

这篇文章的作者是80后博士生王磊光,如今在上海大学文化研究系上学。他平时喜好文学,对乡土问题比较关心,本科毕业后曾在某中学担任语文老师,随后继续考研深造。在这篇笔记中,他描述了自己返乡的见闻:交通没有以前那么拥挤,但家乡人与人之间联系渐渐疏远,而农村里年轻人的婚姻受到了物质的压迫,知识的无力感也十分强烈。

没想到,就是这些在他眼里看似普通的乡村生活见闻,却触动了一大批读者,在春节返乡之际引发了人们对乡土的思考。许多网友都用“感同身受”四字评价,但也人指出返乡笔记有“美化”嫌疑,网友“Walter”评论道:“什么素质,绿皮火车脏乱差,打牌外放音乐严重影响他人,居然还好意思美化……”

对此,王磊光感到很奇怪:“为什么有人读出了美化,我身边的朋友都为我担心,觉得我将家乡不光彩的一面说出来了。至于我提到慢车及其生活状况,从来都没有赞美—我讲述记忆中的交通时,已经说到了慢车上的那种糟糕的情况。我这里提到慢车,目的在于说明它在当下存在的必要性;同时,指出慢车上的那种人与人的联系,是人间应该有的自然联系。”

“知识的无力感”观点引发论争

此文也让中国农村的现状,在春节期间成为社交媒体上的热门话题,并收获诸多回应,有肯定的声音,也有争议之声。前日,一篇署名为“古鱼”的《又一篇博士生返乡笔记:从一而终的稳定生活更可怕》在澎湃新闻发布,以另一位来自乡村的文科博士生视角看乡村。古鱼对农村大学生“近年情更怯”的现象表示不认同,在他看来读大学的观念应该改一改:“无论乡村还是城市,读书不会无用,因为知识是有用的,读过大学的人相对而言会有更高的成长空间,以后贡献越大,拿钱也就越多,而不是一毕业就能兑现很多钱或一毕业就加入体制内获得某种‘人上人’的身份优越感。”

对于返乡笔记引发的争议,王磊光回应道,自己从未说过读书无用,只是强调知识的无力感。“本来读书出来是应该有所为的,但是回到家乡却不能做什么。”

这篇笔记其实是应邀写的演讲稿

除夕之际,王磊光不断接到朋友、以前学生的电话,他才知道自己的返乡笔记在这个春节火了。这是他没有预料到的,他感到不安,也不能理解:“我很少去看别人的评论,因为在我看来,这只是一个媒体事件。”

事实上,这是王磊光应邀为2014年2月举办的“我们的城市论坛”所写的一篇演讲稿。他反问南都记者:“你有没有发现这篇文章就像一个提纲?很多内容和细节没有展开。”春节前,上大现代文学研究所副研究员罗小茗告诉他,媒体有意发表其返乡手记,王磊光答应发表。当时的题目仅是《近年情更怯》,最终见报标题为《一位博士生的返乡笔记:近年情更怯,春节回家看什么》。因此,他把返乡手记爆红的原因归于媒体的传播。

王磊光这篇笔记也获得了自己的导师—上海大学文化研究系教授、主任王晓明的称赞:“分析很真切,文化研究的视野开始有点形成了,好!”博导王晓明表示,读了返乡手记最后一部分“知识的无力感”,也同样感到心情沉重:“从现代早期到上世纪90年代,从农村出来到城市求学的人,总体上是能够以自身的生活和精神状态让其他没有这个机会的人信任‘求学是人生正道’的,有这个信任在,城市里的进步力量反哺乡村的可能就存在。但现在的情况,似乎越来越像80年代晚期90年代初的上海:出租车司机每月赚1400元,当得知我一个大学副教授每月才600元的时候,很同情地看着我:‘算了,下海吧!

“突然出名”让他感觉很不真实

王磊光觉得,自己这篇文章没什么了不起,也不是他理想之作,这种“突然的出名”让他感觉很不真实。网友们不断在网上评论博士生返乡手记,王磊光在手记发布的第一天看了看评论之后,便很少关注。他知道,不管自己说什么,都会有各种评论出现。

更何况,真正生活在文中所述乡村的人们并不知晓博士生返乡手记的走红,王磊光也从来没有向生活其中的长辈们提及文章的事情。“他们都是很本分的人,如果看到我将身边的事情写了出来,一定会为我担心。

对于自己的文章引发的有关农村大学生出路问题的讨论,王磊光则表示,“我所说的,是80后大学生出路难的问题,这里有一个背景,即与上世纪八九十年代大学毕业生相比,当时他们大学毕业之后是能够改变自己甚至家庭的命运,而如今的80后大学生承载着家庭的希望,但绝大部分人的出路是艰难的。”如今,媒体陆续找到他,但王磊光希望人们不要关注他本人,而去关注现实的中国。

附全文

###博士春节返乡手记:越看越对乡村的未来越迷茫

“有故乡的人回到故乡,没有故乡的人走向远方。”我很庆幸我有故乡,可以随时回去,尤其可以回家乡过年。因为我的根在那里,我的亲人在那里,我的生活经验和记忆在那里。

我的家乡在湖北的大别山区,L县。我导师王晓明教授在2004年写过一篇著名的文章《L县见闻》,写的就是这个地方。王老师以我家乡为对象,揭示了当时农村的破产状况,人的精神的颓败,以及乡村文明的没落。我家所在的那个村子,是一个东西两座大山夹住的狭长谷地。一个村子由十来个“塆子”组成,一个塆子有几十户人家,我家那里叫王家塆。

直到现在,我每到一个地方,凡是碰见两山相夹或两排高大的建筑物相夹,我的第一意识就是,这两座山或两排建筑物,一个在东,一个在西,所以我在外面经常迷路,尤其在城市里。上海7号线有两个靠得比较近的站:“长寿路”和“常熟路”。我好几次下错站,以致现在每到这两个站就紧张,怕弄错了。为什么呢?因为在我家乡的方言里,“长寿”和“常熟”是完全一样的读法。人要靠语言来思维,这个事情让我意识到:对有家乡的人来说,是用方言来思维的。

我有一个初中同学群,群里90%的同学只读到初中就出去打工。经过十七八年的积累,很多同学在城市里有房有车,有的还有了自己的事业。平时在群里,他们交流的最多的是工作问题,车子问题等,言谈中总少不了炫耀。

但有一次,有个同学忽然在群里说,他已经三年没回家过年了,另一个同学紧跟着说,他五年没回家了,接着很多人说起回家的情况。有一个说:不管怎么样,今年过年一定要回一次家!另一个说:如果能在家乡找一个两千块钱的工作,就回去算了。还有一个说:能找个一千块的工作,我这边什么都不要,也愿意回家。

我有一个从小学到初中的同学,已经十年没有回家。有一天他在qq里突然对我说,我的父母是很好的人,因为小学四年级的时候他去我家玩,我爸妈用腊肉下面给他吃。这都是陈芝麻烂谷子的小事,他还记得,其实我知道,这是因为他太想家了。

上海大学文化研究系有位老师主持来沪青年工人的社会调查,最近在访谈工人。有一个打工者说:我真希望邓小平没有搞改革开放,我也愿意日子苦些,因为这样我就可以每天跟父母和孩子在一起。

回家过年,其实是没有道理可讲的一件事。套用贾平凹的话来说:家乡对我们的影响,就像乌鸡的乌,那是乌到了骨头里面。

回家的交通

十多年前我上本科的时候,从大西北到武汉,坐的是那种编码没有字母只有四个数字的绿皮火车,22小时,通常要晚点两三个小时。西北往武汉的路线,不是人流最多的,但春运那个挤啊,大大超出了今天90后的想象。好在那个时候,学生一般都可以提前集体订票,买得到座位。而站着回家的,几乎全都是农民工。每次上车的时候,无论有票的还是没票的,都一窝蜂往车上挤。

我对过年回家的第一印象就是:我背着一个包,提着一个包,与同学一起,从第一节车厢狂奔到第十几节车厢,然后被后面的人推着挤上了车。上车后一分钟,车就开动了。我记得火车广播里号召大家发扬风格,让站着的乘客挤一挤。大家真的很友好,四个人的坐位,挤了五六个。火车过道里人贴着人,想蹲下来都没有办法,连厕所里也挤着好几个人。

男乘客还可以想办法,可苦了女乘客。记得有一次我身边坐着一个在西安读书的大学生,他要小便,就脱下外套让我给他挡住身体,想把尿撒在矿泉瓶里,但他很紧张,用了十几分钟才勉强撒出来。我还记得有一次身旁坐着一个从西北打工回家的河南妇女,尽管有位子,但她实在太困,太想睡觉了,就把位子让给别人坐,自己钻到座位底下睡觉去了。

应该要肯定,我们国家这十年间的铁路建设取得了巨大成就,铁路线路的增加,尤其是动车和高铁的开通,极大缓解了交通压力。火车站、火车上,起码不会像过去那么拥挤了。

过年回家那种路途的遥远、时间的漫长、竞争的激烈、拥塞以及不安全感,让我对“男儿有志在四方”的观念产生了极大厌倦。所以,本科毕业时,我找工作坚决要回到湖北。后来我就在家乡隔壁的县城一中当老师。自2004年到2011年来上海读研之前,我再也没有遭受春节回家难的痛苦。尽管从隔壁县回家的汽车在过年时依然被塞得满满的,但毕竟只有两个多小时,实在挤不下,还可以花两百多块钱请出租。我在上海读研的这几年,其实也没有遭受回家难的痛苦,因为上海到武汉的高铁和动车很多,普通车也有几趟,买票很方便。

今天各位出行,如果坐火车,不是高铁就是动车吧?但是,不知道大家有没有想过:那种速度慢、见站停的普通列车是否还有存在的必要?

大家有没有想过:到底是谁在乘坐普通列车?

我想大家肯定一下子就能给出答案:除非没有其他更好的交通工具,学生不会坐,城市人不会坐,主要是那些底层的老百姓,比如农民、农民工在坐。

去年暑假和寒假回家,我特意选择坐慢车,16个多小时的硬座。就是要看看是哪些人在坐慢车,看看慢车上还是不是过去那个样子。的确,主要是农民、农民工在坐慢车。对农民工来说,选择坐慢车,比动车起码节约一半的钱,比高铁节约三分之二以上的钱。从深圳到武汉,高铁一等座要八百多,二等座五百多,但慢车硬座不到两百。尤其对于全家在外打工的人,从深圳到武汉,可能要节约一千多元,这对农民家庭来说不是小数目。

不过,慢车也没有过去那么挤了,因为农民工虽多,但很多都被动车和高铁分流了——既有主动的分流,也有被动的分流,因为价格便宜的慢车越来越少了。

大家可以注意到,今年12306网站通告的春运期间的加班车,三分之二以上的是非动车高铁。这个安排还是挺人性的,因为说到底,加班车就是为了农民工而加,低价位的车符合他们的需求。

而且,你会发现,普通火车与动车的氛围完全不同。

在动车上,相对比较安静,大家不是玩电子产品就是睡觉,相互间很少交流。但是,在普通火车上,熟悉的、不熟悉的,都在热烈地交流,还有打牌、吃东西的,做什么的都有,也有用劣质手机放歌曲的,大家都不担心打扰到别人,也没有人认为别人的做法对自己是一种干扰。慢车上的风格是粗犷的,是人间生活的那种氛围。

对比动车高铁与普通火车,很容易就能发现这里的阶层差别、生活方式的差别。而且你还能感觉到,底层人的心理,比我们想象的要乐观得多,健康得多。底层的状况虽然普遍很糟糕,但大家还是很听话地活着,这里面的一个重要原因,就是他们如果在外面活不下去,还有家园可以退守。

开私家车回家过年,在青年打工者中越来越普遍。我待会进一步讲这个事情,因为它的意义大大超出了交通工具本身。

骑摩托车回家的情况,大家可能在新闻里看到了。每年春节,总有摩托大军回家过年。我的一个表哥,每到过年时就让他的儿子坐汽车回家,而自己骑摩托车带老婆回家,路上要两天一夜。另一个表哥也是骑摩托带老婆回家,有一年在途中撞了人,不知是真撞还是被讹诈了,反正被人家扣了一天多,赔了一万多块才放人,半年的收入就这样没有了。

人与人之间联系的失落

我觉得,当前农村的亲情关系,很大程度上是靠老一辈建立的关系维系着。在老一辈那里,这种关系处在一种相对稳定的时空里,但对年轻一代来说,大家的关系早已被现实割裂了。比如,我和我的众多表哥,小时候一起上山捉鸟,下河摸鱼,关系好得不得了,但这一二十年来,他们一直在外打工,我一直在外读书和工作,一年最多在过年时见一次,平均下来每年还没有一次,因为他们不是年年都回家。拜年的时候,大家也不再像过去那样,在亲戚家吃饭喝酒聊天,甚至留宿一晚,现在大家都骑着摩托车拜年,去亲戚家匆匆走一遭,放下东西,客套几句,就要离开了。平时的生活啊情感啊什么的,都没有来得及交流。大家拜年,不再是为了亲戚间互相走动,馈赠礼物,交流感情,而只是为了完成传统和长辈交代的一项任务

悲哀的是:如果老一辈都不在世了,新一辈的联系也就慢慢断了。

更让人悲哀的是:农村的日常生活充满着深刻的悲剧。自打工潮于九十年代兴起以来,很多农村人一直在外打工,二十多年来与父母团聚的时间,平均到每一年可能就十来天。很多农村老人倒毙在田间地头,病死在床上,儿女都不在身边。没有来得及为父母养老送终,成为许多人终身的悔恨。

每次回家,看到我身边的老人摇摇欲坠的样子,我就觉得心里难受得很。

如果一个人为了生存,连爱父母爱子女的机会都被剥夺了,你怎么可能指望他去爱别人,爱社会,爱自然?你怎么可能指望他能用超出金钱的标准来衡量别人的价值?所以我想说:现代生活是一种让人心肠变硬的生活。

在农村,还有什么可以将农民动员起来?

自从2006年免收农业税之后,中国农村的基层组织主要起着上传下达的作用,不再与农民的根本利益发生关系,也不再能将农民组织起来,农民处于“个人自治”的状态。

(1)春节的力量。亲人团聚,过年拜年。过年的力量,亲情的力量,是当下动员中国人最有效的力量。这也是过年最让人感觉温暖的东西。当然,以前过年时的各种集体活动,都已消失殆尽了。

(2)祭祀。中国农村还是保持着过年、过十五给祖宗上坟“送亮”的习俗——家家户户都要去祖宗的墓地给祖先点蜡烛,烧纸钱,放鞭炮,与祖先交流。很多已经在城市安家的人,也会赶在大家三十这一天开车回老家给祖宗上坟。许多曾被废弃的祠堂,这些年也逐渐恢复起来了。

(3)葬礼。很多老人没有挨过冬天。过年前后,是老人逝世的高峰时段。丧葬在中国文化和中国人的生活中有着非常重要的地位,尤其对今天的社会来说,有着特别重要的意义。媒体上动不动就喜欢报道某某地方为举办葬礼大肆挥霍,让大家误以为这是普遍现象。其实恰恰相反。相比古代,今天的丧葬已是在最大程度上简化了。“贵生重死”的观念早已失衡了——大家越来越贵生,对于死,不再有敬重,不再让死者享受哀荣;对于天地,不再有敬畏。

但丧礼,在现实中依然起着不可替代的作用。去年快过年的时候,本家一个叔叔亡故——本家人和四面八方的亲戚来给他守丧,守丧的时候大家聚在一起交流,像过节一般,交流一年的生活情况、见闻和感想,称赞中央的政策,谴责干部的腐败……深夜里交谈的声音传得很远很远。守丧完毕,大家集体出力,将他抬到山上,让他入土为安。

社会学者经常用“原子化”来形容今天农村的现状,说白了就是,农村原有的那种共同体已经消失了,人与人之间不再像原来那样有着密切的关系和交往,不再像过去那样每到过年时相互串门,集体上街玩等等。为死者守丧和送葬,在农村反而成了村里人团聚和交流的一个契机。这也是我在家乡看到的唯一能够让大家团聚的方式。

妻子?房子?车子

(1)妻子。这一点主要是针对农村的男青年来说的。在今天的社会,农村男青年在本地找媳妇越来越难。一来,这是由中国男多女少的现状决定的。而且,农村稍微长得好看点的女孩子,基本都嫁到城里去了,愿意嫁在农村的女孩子越来越少。二来,农村青年讨媳妇,要具备的物质条件很高,现在普遍的一个情况是:彩礼六到八万,房子两套:在老家一栋楼,在县城一套房。这个压力,并不比城市青年讨老婆的压力小。

过年的时候,打工的青年男女都回来了。只要哪一家有适龄女孩子,去她家的媒人可谓络绎不绝。这在乡村已成了一门生意,农村说亲,几乎到了“抢”的地步。如果初步说定一个,男方至少要给媒人五百块,最终结婚时,还要给上千的报酬,有的甚至要给到两三千。

传统的农村婚姻,从相亲到定亲到结婚,要三四年时间,男女双方有一个了解和熟悉的过程。现在却不同,年里看对的,过了年,马上定亲,然后女青年跟着男青年出去打工,等到半年过去,女方怀孕了,立刻奉子成婚。

曾听过一个搞量化统计的学者对农民工的调查报告,得出的结论之一是:农村孩子结婚越来越迟。但我看到的情况恰恰相反:因为女孩子难找,男孩子一到二十岁,父母就张罗着给儿子物色对象,物色好对象之后,既怕女孩子变心,又考虑要到城市讨生活的现实情况,就催着孩子赶快结婚。可以想象:在现代社会这种动荡不安的生活中,这样的婚姻会出现多少问题!事实上,农村离婚的情况,也是与日俱增的。

(2)房子。刚才已经说了,现在农村人娶老婆要房子两套:一套在家里,一套在县城。其实县城的那套房,平时都空着,只是过年时回来住,但对年轻人来说,那就是城市生活的一种代表。过年时,有的也会把父母接到县城过年,但父母住不惯,在县城过了大年,初一就赶回来了。在老家的生活是“老米酒,蔸子火,除了神仙就是我”,而在县城除了那套房,什么都没有。

但是,为了添置这两套房,将来给儿子娶媳妇,很多家庭是举全家之力在外打工。

下面给大家看我在去年过年的时候写的一则笔记,涉及到房子和婚姻的问题,但还有其他的含义在其中。

2013年冬天的一则笔记

跟大哥、细哥到二郎庙水库捕鱼。(细哥承包的这座小水库只有三十亩的水面,在海拔八九百米的山上,水很纯净,可直接饮用,鱼放在里面长得非常慢,一年下来甚至还要瘦。每年腊月底或者年初,细哥就要从外面进鱼秧,虽说是鱼秧,其实有三斤多一条——这种鱼是在平原地带的池塘里用饲料喂养的,进价是两块多,但是鱼在纯净水里清洗了一年之后,肉味大大改善,可以卖到五块多一斤。)

一个拉砖的师傅把车停在坝上。我们问他,从山下往山上拉一趟要多少钱。他骂了一句话粗话,然后说:“两百块,划不来!”又说,就是这样的生意,也越来越少了。山里的楼房基本都做完了,没有做的也都在县城里买了房。大哥说:“在县城买房又么样,到时住在那里做什么呢?”司机说:“只要是人,总有个生存的法子。”又来了一个人,是细哥的同学,他的摩托车上带着老婆和还在读初中的儿子。得知他在这山里做了楼房,还在县城买了一套房。细哥问:“你要买那么多房做么事!”他叹了一口气:“我们这时代不叫人过的时代!没办法!”“做了一栋楼,买了一套房,还叫没办法!明年还去打工吗?”“不去打工,在家里做么事?”而这座水库上头的两个塆子的人家,基本上都在这里做了一栋楼,在县城买了一套房。

其间来了一人,开小车,戴墨镜,手腕上戴着很粗的黄金链子。老远就用粗嗓子喊正在水上下网的细哥,问有鱼没有。细哥正划着独木船,一只手划,一只手下网,笑着答:“你又不买,问着做么事!”同我们说话时,他的墨镜始终没有摘下来,神气得不得了。墨镜又对细哥喊:“别扑了麻雀(翻船)哈,我是秤砣,到水里就沉了,帮不了你。”说完就独自哈哈地笑。他同我们说起晚上要陪开挖机的斗地主。说是挖山种天麻,规模很大,已经买了十五万斤树。从言谈中得知,他平时在县城住。细哥的同学也说,他准备将家里几面山的树都卖了——分田到户后交了几十年的税,没有沾过任何光。后来听细哥讲,墨镜小学没读完,就在外面混,替人讨债,拿斧头砍人,就这样起家的。

一会儿又来了母女三人,带着一个三岁的小孩。她们是来买鱼的。跟墨镜是亲戚。墨镜却不认识那个年纪最小的女孩。“跟以前长得不一样了呀!”墨镜说,“在哪里打工?”她说在温州。“属什么?”“属鸡。(刚满20)”

墨镜说:“还没有说人家吧?我帮你介绍个。”女孩的母亲说:“她回来这几天,已经有好几个人来介绍。”“某某某正为儿子找媳妇急得哼,我把你说到他家。”(说,替人说亲的意思)女孩母亲连忙说:“那怕是不行,她想嫁到县城里。”墨镜说:“他家在县城有套房子。那男孩的娘脾气不好,但你们又不跟她过,你们到县里住,做点小生意。他家也有钱,你叫他们现在拿个四五十万,轻而易举就拿出来了。”墨镜走的时候,表示过两天,要带那男孩上门相亲。

(3)车子。近些年来,对在外打工五年以上的农村青年来说,对一种东西的渴求,可能比对房子和妻子更为强烈,那就是车子。车子不一定要多么好,五万,八万,二十万,各种档次的都有。老百姓不认识车子的牌子,不知道车子的价位,只知道这些车叫“小车”。不管什么小车,关键是要有!

在农村,房子是一个媒介,车子更是一个媒介——是你在外面混得好,有身份的代表,房子不能移动,车子却可以四处招摇,表示衣锦还乡。很多二代、三代农民工,当下最大的期待就是买一个车子。尤其对那些好些年没回家的人来说,他再次回家,必须要有辆车,否则他怎么证明自己?

春节的县城,到了水泄不通的地步,这些车子绝大部分都是从外面回来的,与此同步的情况是:物价飞涨。

知识的无力感

这十多年来,外界对于农村的关注主要集中于农民工身上。众所周知,他们在城市打工的日子很苦,而家里的老人和孩子往往无人照料。其中酸甜苦辣自不待言。但从另一个角度来看,现在农村日子过得较为殷实的,也恰恰是这些有几个成员在外务工的家庭。(仅仅只有一个成员务工,通常不足以改变家庭的经济状况。)应该说,他们的辛劳和泪水还是得到了适当的回报。

倒是有两类家庭,他们处于最困难的境地,却往往被忽视。一类是孤寡老人。一类是举全家之力,把子女培养成大学生的家庭。

在第一类家庭中,这些老人的年纪一天比一天大,身体一天比一天衰败,没有任何经济来源,日子过得异常艰难。有人会问:国家不是有低保吗?是的,他们中的确有部分人吃上了低保。在我的家乡,低保的额度是每年八百。但是,绝大部分这样的老人,仍在低保的福利之外。因为他们处在农村的最底层,没有人替他们说话。低保名额通常被身强体壮者拿走。甚至,有些村干部为了堵住所谓“刁民”的嘴,不让他们到镇上或县里反映村里的问题,就把这些人变成低保户,有的甚至全家吃上低保。“有钱人吃低保”,早已成为农村公认的一桩怪事。过年的时候,大家也不再像传统社会那样,家家户户给这些孤寡老人送点东西。

这里所谓第二类家庭,主要是指有孩子在1980年代出生的家庭。这些孩子,从小学读到大学,一直都在经受教育收费的最高峰,没有哪一坎能够躲过。并且,二十多年来,农村税费多如牛毛,家里一年的收入,不够交税。大人内外应付,心力交瘁。最要命的是,作为满载家庭希望的大学生,毕业之后勉强找到一份饿不死的工作时,又面临结婚、买房等种种压力。可以说,几乎每一个农村的80后大学生,都是以牺牲整个家庭的幸福为代价来读大学的。但他们中的绝大部分,毕业后没有希望收回成本,倒是让年迈的父母继续陷入困顿。

最近一个博士师兄请吃饭,他说他现在最害怕的就是回家,感觉很难融入到村子的生活,所以他每年过年他都回去得很迟,来学校很早。为什么呢?因为当你一出现在村子里,村里人其他的不问,就问一个问题:“你现在能拿多高的工资?”所以,他过年回家,基本不出门。这个体验跟我是一样的。你要问我过年在家乡看什么,其实我没看什么,因为一大半时间是呆在家里看书,看电视,写东西。

作为农村大学生,当你回到家乡的时候,你童年那些伙伴都衣锦还乡了,而你连自己的问题都不能解决,你还能做什么呢?没有人信任你的知识!

说了上面这些,相信大家能够理解,对于我这样漂在外的农村大学生,回家过年既是一件非常急迫的事情,也是一件情怯的事情。

回家究竟看什么?其实真的没有刻意去观察,但是很多事情却不停地往你心里撞,也就有了很多感受。越看,对乡村的未来越迷茫。

转载自:chinadigitaltimes


2015-03-01 思考 , 感悟 , 转载

Java 查漏补缺之: GC 垃圾回收

说到 Java 很多人脑海会直接蹦出内存自动回收,会经常听到 GC 这些词,GC 指的是 Garbage Collection 也就是垃圾回收。说到垃圾回收就不可避免的去看下 Java 的内存管理机制。

内存管理

提到内存管理可能很多人都会联想起 C/C++ 的手动内存管理,以及 Java/Python 的自动管理,但实际上这都是指的堆内存管理。常规的内存管理可以分成两个部分,栈内存管理和堆内存管理。

栈的发明解决了部分内存的自动回收,但是栈的局限在于只能自动管理固定长度的内存,而对于堆内存,不同语言有不同的管理方式:

  • 纯手动管理 C/C++
  • 自动管理 Java/Python/PHP/C# 等
  • 半自动 C++ 智能指针 Swift/Rust 等

什么是 GC 以及为何要有 GC

对于通常含义上的 GC 可以参考维基词条, John McCarthy 在发明 Lisp 时一并发明了内存自动管理机制。

什么是内存垃圾,也就是程序在执行过程中再无法访问的对象,这些对象所占用的内存空间可以收回来重新使用。

优点:

  • 编码容易
  • 减少因内存管理而导致的 bug,野指针,内存泄露等等

GC 的缺点:

  • 需要消耗额外的 CPU / Memory 资源
  • 代码执行时间无法估计

GC 实现方式

判断对象是否存活一般有两种方式:

  • 引用计数
  • 可达性分析 (Reachability Analysis):从 [[GC Roots]] 开始向下搜索,搜索走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链时,此对象不可用,不可达。

Reference counting

引用计数,最简单也最容易实现的一种,原理是在每个对象中保存该对象的引用计数,当引用发生增减时对计数进行更改。

优点:

  • 当对象不再被引用立即就会被释放,算法运行快
  • 空间释放是针对个别执行,和其他算法相比,GC 中断时间比较短

问题:

  • 额外的内存占用,每个对象需要一个 counter
  • 引用发生增减时需要对计数做出正确的增减,如果发生计数错误可能会导致难以发现的内存错误
  • 循环引用,两个对象互相引用,能解决但需要大量计算
  • 引用计数不适合并行处理,多线程同时对引用计数进行增减时,引用计数可能会产生不一致的问题,而如果采用加锁方式,带来的开销也非常大

引用计数的例子:

  • Python
  • PHP
  • Swift/ OC

Mark and Sweep

标记清除,是最早开发出来的算法(1960 年),原理,从根开始可能被引用的对象用递归的方式进行标记,然后将没有标记的对象作为垃圾回收。

缺点:

  • 效率问题,在分配了大量对象,而只有一小部分存活的情况下,算法消耗的时间多
  • 大量不连续的内存碎片
  • 执行时间不可控

Copy and Collection

复制收集,为了克服标记清除的问题,将内存分为大小相同的两块区域,每次只使用一块,在算法中将根开始被引用的对象复制到另外的空间中,然后将复制的对象所能够引用的对象用递归方式复制下去。

复制收集方式的过程相当于标记清除算法中的标记阶段,但由于清除阶段依然要对所有对象进行扫描,如果存在大量对象,而且大量对象已经死亡的情况,开销会加大。

优点:

  • 没有内存碎片
  • 复制收集过程中,按照对象被引用的顺序将对象复制到新空间,关系较近的对象被放到较近空间的可能性提高,局部性能提升,内存缓存可能更容易命中

缺点:

  • 和标记清除相比,复制对象的开销加大,当存活对象较多的情况下,性能损耗
  • 对于长时间存活的对象将反复拷贝,效率低

Mark-Compact

标记-整理算法,复制算法在对象存活率较高的时候需要反复执行多次拷贝,并且需要浪费 50% 空间。所以老年代一般不用。

根据老年代的特征,提出了标记-整理算法,标记过程和标记清除算法一致,后续不是对可回收的对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉边界以外的内存。

分代收集算法

Generational Collection 算法,分为新生代和老年代,根据不同年代的特征选择不同的收集算法。

新生代中有大量对象死去,只有少量存活,就使用复制算法。

而老年代对象存活率较高,使用标记清除或标记整理算法。

JVM 如何实现 GC

现代版的 GC 使用分代收集,按照对象存活时间长短来使用不同的垃圾回收算法。

Heap 堆内存分为:

  • Young Generation:新创建的对象,Young Generation 又被分成 Eden space (所有新对象开始的地方),两个 Survivor spaces(在存活一个 gc 之后移动到这里),当对象在 Young Generation 被回收,这是 minor garbage collection event(简称 minor gc)。
  • Old Generation: 当对象存活足够长时间,会从 Young Generation 移动到 Old Generation。当对象在 Old Generation 被回收,这是一次 major garbage collection event.
  • Permanent Generation: 类,方法等 Metadata 会保存在 Permanent Generation。不再被使用的 Classes (ClassLoader 回收后)会被回收。

所以一个对象在不同分区的流程可能是:

  • 新对象在 Eden 中创建
  • Eden 满时进入 Survivor spaces
  • 两个 Survivor 空间的对象相互交换
  • 在 Survivor 存活一定时间后进入 Old

一种 JVM 的实现,由 1999 年引入,HotSpot 为代表,HotSpot JVM 有四种 Garbage Collector:

  • Serial GC : 所有的 garbage collection events 通过一个线程连续管理, Compaction 在每一次 garbage collection 之后执行
  • Parallel GC : 并行进行 minor garbage collection,一个线程用来 major garbage collection 和 Old Generation compaction
  • Concurrent-Mark-and-Sweep GC : 简称 CMS,多个线程用来 minor garbage collection ,使用和 Parallel 相同的算法。CMS 在应用程序之外运行,GC 和 应用程序并行,减少中断。不会执行 compaction
  • G1 GC(1.7+) : Garbage First,新的 garbage collector,用来替换 CMS,同样是并行并发的,但是原理和工作方式完全不一样

Java 的 GC 是不确定的,没有方法来预测何时会发生 gc。在代码中可以使用 System.gc() 或者 Runtime.gc() 方法来暗示 gc,但是 Java 不能保证 gc 一定会执行。

GC 的调优可以从 JVM 的参数调节:

  • -Xms 初始堆大小,可以设置和 Xmx 相同,避免每次垃圾回收后 JVM 重新分配内存
  • -Xmx 最大堆大小
  • -XX:NewRatio=n Young 和 old 区的大小比 1:n
  • -XX:MaxPermSize Permanent 大小
  • -XX:+UseG1GC 使用 G1
  • -XX:MaxGCPauseMillis=n 最大希望暂停时间
  • -XX:InitiatingHeapOccupancyPercent=n 堆使用到多少百分比时开始 CMS 过程
  • -XX:+PrintGC 输出 GC 日志
  • -XX:+PrintGCDetails 输出 GC 的详细日志
  • -XX:+PrintGCTimeStamps 输出 GC 时间戳
  • -XX:+PrintGCDateStamps 输出 GC 时间戳(日期形式,2019-01-01T01:01:02.212+0800)
  • -XX:+PrintHeapAtGC 进行 GC 的前后打印堆信息
  • -verbose:gc
  • -XX:+PrintReferenceGC 年轻代各个引用的数量以及时长
  • -Xloggc:../logs/gc.log 日志文件输出路径

JVM 内存区域划分

Heap 区

  • Eden
  • Survivor
  • Old gen

非 Heap 区:

  • Code Cache, 代码缓存区,它主要用于存放 JIT 所编译的代码
  • Perm Gen,Permanent Generation space,是指内存的永久保存区域
  • Jvm Stack
  • Local Method Stack

为什么需要 Survivor 区

前提知识,新生代内存中除了 Eden 区外还有两个 Survivor 区,Eden 占 80%,两块 Survivor 区占比 20%。

如果没有 Survivor ,Eden 区每进行一次 Minor GC,存活的对象都会被送到老年区,老年代很快被填满。

每进行一次 Minor GC,存活下来的对象会被计数 +1,当对象在 Minor GC 下存活多次,达到一个阈值后会被移动到老年代。

Survivor 存在的意义,减少被送到老年代的对象,减少 Full GC 发生,只有经历 16 次 Minor GC 还能在新生代中存活的对象,才会被送到老年代。

JVM 参数设置和调优

生产环境 Xms 和 Xmx 设置相同的值

In a production environment, if you monitor the GC data, you will notice that is a relatively short period of time (usually less than an hour), the JVM will eventually increase the heap size to the -Xmx setting. Each time the JVM increases the heap size it must ask the OS for additional memory, which takes time (and thus adds to the response time of any requests that were is process when the GC hit). And usually the JVM will never let go of that memeory. Therefore, since the JVM will eventually grab the -Xmx memory, you might as well set it to that at the beginning.

Another point is that with a smaller heap size (starting with -Xms), GCs will happen more often. So by starting with a larger heap initially the GCs will happen not as often.

Finally, in a production environment, you usually run only one app server per OS (or per VM). So since the app server is not competing for memory with other apps you might as well give it the memory up front.

Note that the above is for production. It applies also to the syatem test environment since the system test environment should mimic production as close as possible.

For development, make -Xms and -Xmx different. Usually, you are not doing much work with the app server in development, so it will often stay with the -Xms heap setting. Also, since in development the app server will share the machine with lots of other apps (mail client, word processors, IDEs, databases, browsers, etc), setting the -Xms to a smaller size lets the app server play more nicely with the other software that is also competing for the same resources.

gc log

enable gc log

通过如下任意一个开启 gc log:

-XX:+PrintGC
-XX:+PrintGCDetails
-Xloggc:gc.log

开启 -XX:+PrintGC 后,打印日志:

[GC (Allocation Failure)  61905K->9848K(256000K), 0.0040139 secs]

说明:

  • GC 表示是一次 YGC(Young GC)
  • Allocation Failure 表示是失败
  • 61905KK->9848K 表示年轻代从 61905KK 降为 9848K
  • 256000K 表示整个堆的大小
  • 0.0040139 secs 表示这次 GC 总计所用的时间

开启 -XX:+PrintGCDetails 后,日志:

2020-03-28T08:55:24.916+0800: 229805.169: [GC (Allocation Failure) 2020-03-28T08:55:24.916+0800: 229805.170: [ParNew: 584336K->24291K(629120K), 0.0145141 secs] 1849190K->1289986K(2027264K), 0.0155393 secs] [Times: user=0.08 sys=0.02, real=0.02 secs]

说明:

  • 第一个时间戳 2020-03-28T08:55:24.916+0800 是 Mirror GC 发生的时间
  • 229805.169 GC 开始的时间,这里是相对 JVM 启动时间,单位秒
  • GC 用来区分 GC 类型,Minor GC 或者 Full GC
  • Allocation Failure 分配内存失败
  • ParNew 收集器名称
  • 584336K->24291K 前后年轻代使用
  • 629120K 整个年轻代的容量
  • 跟随的时间是 gc 发生的时间
  • 1849190K->1289986K 堆前后使用情况
  • 后接的时间是 ParNew 收集器标记和复制年轻代存活的对象的时间

    最后出现的 [Times] 中三个时间是:

  • user: GC 线程在垃圾收集中使用 CPU 时间
  • sys: 系统调用时间
  • real: 应用被暂停的时钟时间,GC 是多线程的所以, real < (user + sys)

Example

public class GCLogDemo {

	public static void main(String[] args) {
		int _1m = 1024 * 1024;
		byte[] data = new byte[_1m];
		// 将 data 置为 null 即让它成为垃圾
		data = null;
		// 通知垃圾回收器回收垃圾
		System.gc();
	}
}

相关命令

jstat

jstat 可以用来查看 JVM 数据信息:

jstat [options] vmid [interval] [count]
  • options 使用 -gc 或者 -gcutil
  • 这里的 vmid 是 vm 的进程号,当前运行的 java 进程号

比如查看 gc 情况

jstat -gc [PID]

每 5 秒打印一次特定 PID 的 GC 情况

jstat -gc [PID] 5000
jstat -gc [PID] 5s

结果说明:

前提知识,新生代内存中除了 Eden 区外还有两个 Survivor 区,Eden 占 80%,两块 Survivor 区占比 20%。

S0C:当前年轻代中第一个 survivor(幸存区)的容量 (字节) ,简记成 Survivor 0 Current
S1C:当前年轻代中第二个 survivor(幸存区)的容量 (字节) 
S0U:年轻代中第一个 survivor(幸存区)目前已使用空间 (字节) 
S1U:年轻代中第二个 survivor(幸存区)目前已使用空间 (字节) 
EC:当前年轻代中 Eden(伊甸园)的容量 (字节) 
EU:年轻代中 Eden(伊甸园)目前已使用空间 (字节) 
OC:当前 Old 代的容量 (字节) 
OU:Old 代目前已使用空间 (字节) 
PC:当前 Perm(持久代)的容量 (字节) 
PU:Perm(持久代)目前已使用空间 (字节) 
YGC:从应用程序启动到采样时年轻代中 gc 次数 
YGCT:从应用程序启动到采样时年轻代中 gc 所用时间 (s) 
FGC:从应用程序启动到采样时 old 代(全 gc)gc 次数 
FGCT:从应用程序启动到采样时 old 代(全 gc)gc 所用时间 (s) 
GCT:从应用程序启动到采样时 gc 用的总时间 (s) 
NGCMN:年轻代 (young) 中初始化(最小)的大小 (字节) 
NGCMX:年轻代 (young) 的最大容量 (字节) 
NGC:年轻代 (young) 中当前的容量 (字节) 
OGCMN:old 代中初始化(最小)的大小 (字节) 
OGCMX:old 代的最大容量 (字节) 
OGC:old 代当前新生成的容量 (字节) 
PGCMN:perm 代中初始化(最小)的大小 (字节) 
PGCMX:perm 代的最大容量 (字节)   
PGC:perm 代当前新生成的容量 (字节) 
S0:年轻代中第一个 survivor(幸存区)已使用的占当前容量百分比 
S1:年轻代中第二个 survivor(幸存区)已使用的占当前容量百分比 
E:年轻代中 Eden(伊甸园)已使用的占当前容量百分比 
O:old 代已使用的占当前容量百分比 
P:perm 代已使用的占当前容量百分比 
S0CMX:年轻代中第一个 survivor(幸存区)的最大容量 (字节) 
S1CMX :年轻代中第二个 survivor(幸存区)的最大容量 (字节) 
ECMX:年轻代中 Eden(伊甸园)的最大容量 (字节) 
DSS:当前需要 survivor(幸存区)的容量 (字节)(Eden 区已满) 
TT: 持有次数限制 
MTT : 最大持有次数限制 

如果使用 -gcutil 则是打印 GC 的使用率:

jstat -gcutil -h 10 [PID] 1000

jmap

使用 jmap 打印堆相关信息,更多细节可以参考这篇文章

jhat

更多关于 jhat 的用法可以参考这篇

reference


2015-02-27 java , gc , garbage-collection

每天学习一个命令:xclip 与剪贴板交互

xclip 命令可以从 stdin,或者文件读入数据到剪贴板,或者将剪贴板内容粘贴到目的应用中。xclip 命令建立了终端和剪切板之间通道,可以用命令的方式将终端输出或文件的内容保存到剪切板中,也可以将剪切板的内容输出到终端或文件

安装

sudo apt-get xclip

命令格式

xclip [OPTION] [FILE] ...

常用参数:

-i      从 stdin 读入
-o      打印到标准输出

使用实例

不加选项时只在保存在 X PRIMARY(终端剪切板),加上选项 -selection c 后保存在 X CLIPBOARD(外部程序剪切板)

为了区分这二者的区别,可以简单的做一个试验。

echo "Hello World" | xclip

此时 Hello World 字符只是在终端的剪贴板中,可以尝试在终端鼠标中键粘贴,发现终端的粘贴板是已经被修改的,此时用 Ctrl + v 粘贴到其他 GUI 应用程序(比如 Chrome 地址栏)发现粘贴板并不是 Hello World。

echo "TEST OUTSIDE CLIPBOARD" | xclip -sel c

此时会发现 Chrome 中可以粘贴 TEST 这行文本,而鼠标中键粘贴到终端的还是上面的 Hello World。

终端输出保存到剪切板中

ls -al | xclip
echo "SOME" | xclip
xclip /etc/passwd
xclip < /etc/passwd

此时 ls -al 的输出内容已经保存在剪切板中了,此时 xclip -o 可以看到剪切板的内容。

但此时还不可以粘贴到终端以外的程序中,此时需要用到: xclip -selection c

ls -al | xclip -selection c
xclip -sel c /etc/passwd
xclip -sel c < /etc/passwd

剪切板内容输出到终端

xclip -o
xclip -selection c -o

剪切板内容输出到文件

xclip -o > ~/test.txt
xclip -selection c -o > ~/test.txt

reference

  • man xclip

2015-02-26 linux , command , xclip

Java 的 IO 操作 java.io 包

InputStream 和 Reader 的区别

  • InputStream 是 byte 导向
  • Reader Writer 是字符导向

2015-02-20 java , java-io , io

电子书

本站提供服务

最近文章

  • Glance 个人自定义 Dashboard Glance 是一个可以自行架设的个人 Dashboard 以及 RSS 订阅信息面板。
  • Fileball 一款 iOS tvOS 上的媒体播放器及文件管理器 Fileball 是一款 iOS,tvOS 上的本地文件管理器,本地音乐播放器,本地视频播放器,以及文本编辑器,Fileball 可以在 iPhone,iPad,Apple TV 上使用。Fileball 可以连接网络共享,支持 SMB,FTP,SFTP,Synology,NFS,WebDAV 等,支持 Emby,Jellyfin 等,还可以连接百度网盘,Box,Dropbox,Google Drive,OneDrive,pCloud 等,可以作为 [[Infuse]] ,[[VidHub]] 等播放器的平替,高级版本价格也比较合适。Fileball 也支持 [[IPTV]]。
  • 在日本申请入台证材料及在线提交注意事项 本文记录入台证办理的材料及提交手续,以及在使用线上提交系统的时候需要注意的点。入台证是中华民国台湾地区出入境许可证的俗称,所有进入台湾的人都需要申请此许可证。
  • 从 Buffer 消费图学习 CCPM 项目管理方法 CCPM(Critical Chain Project Management)中文叫做关键链项目管理方法,是 Eliyahu M. Goldratt 在其著作 Critical Chain 中踢出来的项目管理方法,它侧重于项目执行所需要的资源,通过识别和管理项目关键链的方法来有效的监控项目工期,以及提高项目交付率。
  • AI Shell 让 AI 在命令行下提供 Shell 命令 AI Shell 是一款在命令行下的 AI 自动补全工具,当你想要实现一个功能,敲一大段命令又记不住的时候,使用自然语言让 AI 给你生成一个可执行的命令,然后确认之后执行。