正向代理和反向代理

正向代理

A同学在大众创业、万众创新的大时代背景下开启他的创业之路,目前他遇到的最大的一个问题就是启动资金,于是他决定去找马云爸爸借钱,可想而知,最后碰一鼻子灰回来了,情急之下,他想到一个办法,找关系开后门,经过一番消息打探,原来A同学的大学老师王老师是马云的同学,于是A同学找到王老师,托王老师帮忙去马云那借500万过来,当然最后事成了。不过马云并不知道这钱是A同学借的,马云是借给王老师的,最后由王老师转交给A同学。这里的王老师在这个过程中扮演了一个非常关键的角色,就是代理,也可以说是正向代理,王老师代替A同学办这件事,这个过程中,真正借钱的人是谁,马云是不知道的,这点非常关键。

我们常说的代理也就是只正向代理,正向代理的过程,它隐藏了真实的请求客户端,服务端不知道真实的客户端是谁,客户端请求的服务都被代理服务器代替来请求,科学上网工具 Shadowsocks 扮演的就是典型的正向代理角色。在天朝用浏览器访问 www.google.com 时会被无情的墙掉,要想翻阅这堵墙,你可以在国外用 Shadowsocks 来搭建一台代理服务器,让代理帮我们去请求 www.google.com,代理再把请求响应结果再返回给我。

proxy

反向代理

大家都有过这样的经历,拨打10086 客服电话,一个地区的 10086 客服有几个或者几十个,你永远都不需要关心在电话那头的是哪一个,叫什么,男的,还是女的,漂亮的还是帅气的,你都不关心,你关心的是你的问题能不能得到专业的解答,你只需要拨通了10086 的总机号码,电话那头总会有人会回答你,只是有时慢有时快而已。那么这里的 10086 总机号码就是我们说的反向代理。客户不知道真正提供服务的人是谁。

反向代理隐藏了真实的服务端,当我们访问 www.baidu.com 的时候,就像拨打 10086 一样,背后可能有成千上万台服务器为我们服务,但具体是哪一台,你不知道,也不需要知道,你只需要知道反向代理服务器是谁就好了,www.baidu.com 就是我们的反向代理服务器,反向代理服务器会帮我们把请求转发到提供真实计算的服务器那里去。Nginx 就是性能非常好的反向代理服务器,它可以用来做负载均衡。

reverse-proxy

两者的区别在于代理的对象不一样,「正向代理」代理的对象是客户端,「反向代理」代理的对象是服务端

转载请注明来源:正向代理与反向代理

java序列化与持久化

序列化:

序列化 (Serialization)将对象的状态信息转换为可以存储或传输的形式的过程。

transient

transient是类型修饰符,只能用来修饰字段。在对象序列化的过程中,标记为transient的变量不会被序列化。

当某个字段被声明为transient后,默认序列化机制就会忽略该字段。
public class Person implements Serializable {
private String name;
transient private int age;
private String sex;
}
(保存时没有该字段)
只有实现了Serializable和Externalizable接口的类的对象才能被序列化。Externalizable接口继承自 Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serializable接口的类可以 采用默认的序列化方式 。

持久化:

持久化是将程序数据在持久状态和瞬时状态间转换的机制。通俗的讲,就是瞬时数据(比如内存中的数据,是不能永久保存的)持久化为持久数据(比如持久化至数据库中,能够长久保存)。
volatile
volatile也是变量修饰符,只能用来修饰变量。volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。

Java的内存机制:

Java使用一个主内存来保存变量当前值,而每个线程则有其独立的工作内存。线程访问变量的时候会将变量的值拷贝到自己的工作内存中,这样,当线程对自己工作内存中的变量进行操作之后,就造成了工作内存中的变量拷贝的值与主内存中的变量值不同。

Java语言规范中指出:为了获得最佳速度,允许线程保存共享成员变量的私有拷贝,而且只当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。

这样当多个线程同时与某个对象交互时,就必须要注意到要让线程及时的得到共享成员变量的变化。

而volatile关键字就是提示VM:对于这个成员变量不能保存它的私有拷贝,而应直接与共享成员变量交互。

使用建议:在两个或者更多的线程访问的成员变量上使用volatile。当要访问的变量已在synchronized代码块中,或者为常量时,不必使用。

由于使用volatile屏蔽掉了VM中必要的代码优化,所以在效率上比较低,因此一定在必要时才使用此关键字。

linux常用命令的英文单词缩写

命令缩写:

lslist(列出目录内容)

cdChange Directory(改变目录)

su:switch user 切换用户
rpm:redhat package manager
红帽子打包管理器
pwd:print work directory
打印当前目录 显示出当前工作目录的绝对路径
ps: process status(
进程状态,类似于windows的任务管理器) 常用参数:-auxf
ps -auxf
显示进程状态
df: disk free
其功能是显示磁盘可用空间数目信息及空间结点信息。换句话说,就是报告在任何安装的设备或目录中,还剩多少自由的空间。
rpm
: 即RedHat Package Management,是RedHat的发明之一

rmdirRemove Directory(删除目录)

rmRemove(删除目录或文件)

cat: concatenate连锁 cat file1 file2>>file3把文件1和文件2的内容联合起来放到file3
insmod: install module,
载入模块
ln -s : link -soft
创建一个软链接,相当于创建一个快捷方式

mkdirMake Directory(创建目录

touch

man: Manual
pwd
Print working directory
su
Swith user
cd
Change directory
ls
List files
ps
Process Status
mkdir
Make directory
rmdir
Remove directory
mkfs: Make file system
fsck
File system check
cat: Concatenate
uname: Unix name
df: Disk free
du: Disk usage
lsmod: List modules
mv: Move file
rm: Remove file
cp: Copy file
ln: Link files
fg: Foreground
bg: Background
chown: Change owner
chgrp: Change group
chmod: Change mode
umount: Unmount
dd:
本来应根据其功能描述“Convert an copy”命名为“cc”,但“cc”已经被用以代表“C Complier”,所以命名为“dd”
tar
Tape archive
ldd
List dynamic dependencies
insmod
Install module
rmmod
Remove module
lsmod
List module
文件结尾的“rc”(如.bashrc.xinitrc等):Resource configuration
Knnxxx / Snnxxx
(位于rcx.d目录下):KKill);S(Service)nn(执行顺序号);xxx(服务标识)
.a
(扩展名a):Archivestatic library
.so
(扩展名so):Shared objectdynamically linked library
.o
(扩展名o):Object filecomplied result of C/C++ source file
RPM
Red hat package manager
dpkg
Debian package manager
apt
Advanced package toolDebian或基于Debian的发行版中提供)

部分Linux命令缩

bin = BINaries #下面的是一些二进制程序文件

/dev = DEVices  #下面的是一些硬件驱动

/etc = ETCetera #目录存放着各种系统配置文件, 类似于windows下的system

/lib = LIBrary

/proc = PROCesses

/sbin = Superuser BINaries

/tmp = TeMPorary

/usr = Unix Shared Resources 

/var = VARiable ?

/boot=boot #下面的是开机启动文件

FIFO = First In, First Out

GRUB = GRand Unified Bootloader

IFS = Internal Field Seperators

LILO = LInux LOader

MySQL = My是最初作者女儿的名字,SQL = Structured Query Language

PHP = Personal Home Page Tools = PHP Hypertext Preprocessor

PS = Prompt String

Perl = “Pratical Extraction and Report Language” = “Pathologically Eclectic Rubbish Lister”

Python 得名于电视剧Monty Python’s Flying Circus

Tcl = Tool Command Language

Tk = ToolKit

VT = Video Terminal

YaST = Yet Another Setup Tool

apache = “a patchy” server

apt = Advanced Packaging Tool

ar = archiver

as = assembler

awk = “Aho Weiberger and Kernighan” 三个作者的姓的第一个字母

bash = Bourne Again SHell

bc = Basic (Better) Calculator

bg = BackGround

biff = 作者Heidi StettnerU.C.Berkely养的一条狗,喜欢对邮递员汪汪叫。

cal = CALendar

cat = CATenate

cd = Change Directory

chgrp = CHange GRouP

chmod = CHange MODe

chown = CHange OWNer

chsh = CHange SHell

cmp = compare

cobra = Common Object Request Broker Architecture

comm = common

cp = CoPy

cpio = CoPy In and Out

cpp = C Pre Processor

cron = Chronos 希腊文时间

cups = Common Unix Printing System

cvs = Current Version System

daemon = Disk And Execution MONitor

dc = Desk Calculator

dd = Disk Dump

df = Disk Free

diff = DIFFerence

dmesg = diagnostic message

du = Disk Usage

ed = editor

egrep = Extended GREP

elf = Extensible Linking Format

elm = ELectronic Mail

emacs = Editor MACroS

eval = EVALuate

ex = EXtended

exec = EXECute

fd = file descriptors

fg = ForeGround

fgrep = Fixed GREP

fmt = format

fsck = File System ChecK

fstab = FileSystem TABle

fvwm = F*** Virtual Window Manager

gawk = GNU AWK

gpg = GNU Privacy Guard

groff = GNU troff

hal = Hardware Abstraction Layer

joe = Joe’s Own Editor

ksh = Korn SHell

lame = Lame Ain’t an MP3 Encoder

lex = LEXical analyser

lisp = LISt Processing = Lots of Irritating Superfluous Parentheses

ln = LiNk

lpr = Line PRint

ls = list

lsof = LiSt Open Files

m4 = Macro processor Version 4

man = MANual pages

mawk = Mike Brennan’s AWK

mc = Midnight Commander

mkfs = MaKe FileSystem

mknod = MaKe NODe

motd = Message of The Day

mozilla = MOsaic GodZILLa

mtab = Mount TABle

mv = MoVe

nano = Nano’s ANOther editor

nawk = New AWK

nl = Number of Lines

nm = names

nohup = No HangUP

nroff = New ROFF

od = Octal Dump

passwd = PASSWorD

pg = pager

pico = PIne’s message COmposition editor

pine = “Program for Internet News & Email” = “Pine is not Elm”

ping = 拟声 又 = Packet InterNet Grouper

pirntcap = PRINTer CAPability

popd = POP Directory

pr = pre

printf = PRINT Formatted

ps = Processes Status

pty = pseudo tty

pushd = PUSH Directory

pwd = Print Working Directory

rc = runcom = run command, rc还是plan9shell

rev = REVerse

rm = ReMove

rn = Read News

roff = RunOFF

rpm = RPM Package Manager = RedHat Package Manager

rsh, rlogin, rvim中的r = Remote

rxvt = ouR XVT

seamoneky =

sed = Stream EDitor

seq = SEQuence

shar = SHell ARchive

slrn = S-Lang rn

ssh = Secure SHell

ssl = Secure Sockets Layer

stty = Set TTY

su = Substitute User

svn = SubVersioN

tar = Tape ARchive

tcsh = TENEX C shell

tee = T (T形水管接口)

telnet = TEminaL over Network

termcap = terminal capability

terminfo = terminal information

tex = τέχνη的缩写,希腊文art

tr = traslate

troff = Typesetter new ROFF

tsort = Topological SORT

tty = TeleTypewriter

twm = Tom’s Window Manager

tz = TimeZone

udev = Userspace DEV

ulimit = User’s LIMIT

umask = User’s MASK

uniq = UNIQue

vi = VIsual = Very Inconvenient

vim = Vi IMproved

wall = write all

wc = Word Count

wine = WINE Is Not an Emulator

xargs = eXtended ARGuments

xdm = X Display Manager

xlfd = X Logical Font Description

xmms = X Multimedia System

xrdb = X Resources DataBase

xwd = X Window Dump

yacc = yet another compiler compiler

Fish = the Friendly Interactive SHell

su = Switch User

MIME = Multipurpose Internet Mail Extensions

ECMA = European Computer Manufacturers Association

原文地址:http://blog.chinaunix.net/uid-27164517-id-3299073.html

linux 下开发常用命令

************文件和目录*****************

 

pwd

查看自己所在位置

pwd -p /*显示链接到的真实路径*/

ls

列出目录及文件的属性信息

ls -a /*列出所有文件和子目录,包括隐藏文件和目录*/

ls -A /*列出所有文件和目录,但不包括隐藏文件和目录*/

ls -d /*知县是目录本深的属性信息*/

ls -l /*以长格式显示文件和目录的详细信息*/

ls -i /*先是文件的i节点编号*/

ls -h /*显示信息是更人性化容量的大小,如KB,MB,GB等*/

ls -R /*递归显示制定目录下的各级目录及文件*/

ll -h:列表显示目录下的文件,并且显示文件大小.

cat

用于显示指定文件的内容

cat -n /*显示文件内容是在每一行行首添加行号*/

cat -b /*显示文件内容是为非空白添加行号*/

tail

一直跟踪显示文件变化(查看文件末尾指定行数的内容)

tail -N /*N为用户指定的行数*/

tail -f /*跟踪文件中新增加的内容*/

head

用来显示文件开头n行

head -N /*N为用户指定的行数*/

more

分页显示文件内容   (通过空格向下翻页,通过回车一行一行滚动)

more -N /*N为每屏指定的行数*/

less

分页显示文件内容(和more一样。more只能向下翻页,less可以向上翻页。可以通过上下箭头进行上下一行行滚动,也可以通过page up/空格命令向上/向下进行一屏一屏滚动.ctrl+b,用关键字查询按‘n’键可以跳转到下一个关键字)

less 在查看之前不会加载整个文件。可以尝试使用 less 和 vi 打开一个很大的文件,你就会看到它们之间在速度上的区别。

在 less 中导航命令类似于 vi。本文中将介绍一些导航命令以及使用 less 的其它一些技巧。

1 搜索

当使用命令 less file-name 打开一个文件后,可以使用下面的方式在文件中搜索。搜索时整个文本中匹配的部分会被高亮显示。

向前搜索

/ – 使用一个模式进行搜索,并定位到下一个匹配的文本

n – 向前查找下一个匹配的文本

N – 向后查找前一个匹配的文本

向后搜索

? – 使用模式进行搜索,并定位到前一个匹配的文本

n – 向后查找下一个匹配的文本

N – 向前查找前一个匹配的文本

2 全屏导航

ctrl + F – 向前移动一屏

ctrl + B – 向后移动一屏

ctrl + D – 向前移动半屏

ctrl + U – 向后移动半屏

3 单行导航

j – 向前移动一行

k – 向后移动一行

4 其它导航

G – 移动到最后一行

g – 移动到第一行

q / ZZ – 退出 less 命令

5 其它有用的命令

v – 使用配置的编辑器编辑当前文件

h – 显示 less 的帮助文档

&pattern – 仅显示匹配模式的行,而不是整个文件

grep

在文件中查找并显示包含指定字符串的行,查找字符串可使用正则表达式匹配

grep -i /*查找内容是忽略大小写*/

grep -v /*反转查找,技术处于查找条件不相符的行*/

find

在子目录中搜索匹配的文件

find -name /*按文件名称查找,可以只用通配符*/

find -size /*按万大小查找,可以使用K,M等容量单位*/

find -type /*按文件类型查找,类型f,d,l,b,c分别表示普通文件,目录,链接文件,块设备文件,字符设备文件*/

cd

更改工作目录,不带任何选项或参数时,默认切换到用户宿主目录

cd -p /*如果指定的目录为符号链接,则切换到对应的物理路径*/

whereis

查找文件

locate

定位文件

mkdir

创建新目录

mkdir -p /*递归创建多级目录*/

touch

更新文件的时间标记,如文件不存在则建立对应的空文件

cp

复制文件或目录,复制源是目录或多个文件,目标必须是目录

cp -f /*直接强制复制不进行提醒*/

cp -i /*复制时进行提醒确认*/

cp -l /*为源文件建立硬链接,而不是直接复制磁盘数据块*/

cp -p /*复制时保持原文件权限,宿主及时间*/

cp -r /*复制目录时,递归复制所有文件及子目录*/

mv

移动文件或目录,可以在移动时改名。移动多个文件或目录时,目标必须是目录

mv -f /*直接强制移动不进行提醒*/

mv -i /*移动时进行提醒确认*/

rm

删除文件或目录

mv -f /*直接强制删除而不进行提醒*/

mv -i /*删除是时进行提醒确认*/

mv -r /*递归删除所有文件及目录*/

*******************压缩和解压*************

tar

linux传播的文件基本是压缩文件tgz,tar.gz

-c(生成)

-f(文件)

-v(verbose)

-z(压缩)

-t(测试)

-x(展开)

#tar cz(v)f mydir.tar.gz mydir

#tar xz(v)f mydir.tar.gz mydir

–使用示例:

tar -tvzf 20140221130101.log.tgz  –查看该压缩包下是否有文件

tar xfv 20140221130101.log.tgz     –解压到当前文件夹下

******************vi******************

linux/unix下的配置文件都是文本文件

vi是使用最广泛的编辑器

vi分为三种工作模式

-一般模式

-编辑模式

-命令模式

vi常用命令

w q wq wq!

****************远程管理****************

telnet/ssh

ssh使用:

# ssh user1@192.168.0.1 输入密码

scp

 

secure copy (remote file copy program) 也是一个基于SSH安全协议的文件传输命令。与sftp不同的是,它只提供主机间的文件传输功能,没有文件管理的功能。

复制local_file 到远程目录remote_folder下

scp local_file remote_user@host:remote_folder

复制local_folder 到远程remote_folder(需要加参数 -r 递归)

scp –r local_folder remote_user@host:remote_folder

以上命令反过来写就是远程复制到本地

[weblogic@test testdomain]$ scp bbb.txtweblogic@10.10.236.59:/weblogic/wls1036/user_projects/domains/mydomain

 

sz/rz

sz/rz 是基于ZModem传输协议的命令。对传输的数据会进行核查,并且有很好的传输性能。使用起来更是非常方便,但前提是window端需要有能够支持ZModem的telnet或者SSH客户端,例如secureCRT。

首先需要在secureCRT中可以配置相关的本地下载和上传目录,然后用rz、sz命令即可方便的传输文件数据。

下载数据到本地下载目录:sz filename1 filename2 …

上传数据到远程:执行rz –be 命令,客户端会弹出上传窗口,用户自行选择(可多选)要上传的文件即可。

 

************系统相关***********************

ps

以静态快照方式输出当前运行的进程的状态统计数据

格式:ps [选项]

ps a /*显示当前终端下的所有进程信息,包括其他用户信息*/

ps u /*使用以用户为主的格式输出进程信息*/

ps x /*显示当前用户在所有终端下的进程信息*/

ps -e /*显示系统内所有进程信息*/

ps -l /*使用长格式显示进程信息*/

ps -f /*使用完整格式显示进程信息*/

top   动态显示当前运行的进程信息

对于多个或者多核的CPU,top显示的是多个CPU所占用的百分比总和,进入top视图后按1可以按核来显示消耗情况。

us表示用户进程占用百分比,sy表示内核线程(系统线程)处理所占百分比,ni表示nice命令改变优先级的任务所占的百分比,id表示CPU空闲时所占的百分比,wa表示为在执行过程中等待IO所占的百分比,hi表示为硬件中断所占的百分比,si表示为软件中断所占的百分比.

在top视图中按shift+h可以按线程查看CPU的消耗情况。默认情况top视图显示的为进程的CPU消耗情况.

格式:top [-d] 时间

top -d /*指定动态信息的刷新间隔时间,单位为秒。也可以使用ss.tt格式*/

pidstat:

pidstat是sysstat中的工具如需要使用pidstat必须先安装sysstat工具。

如:pidstat 1 2

pidstat -p[PID] -t 1 5

vmstat:

pstree

以树型结构查看系统中的进程及其相互关系(进程数)

格式:pstree [-aup]

pstree -a /*显示进程对应的命令行*/

pstree -u /*显示进程对应的用户名信息*/

pstree -p /*显示进程对应的进程号信息*/

kill

终止指定进程号的进程,PID可以通过ps命令获得

格式:kill [-9] <PID号>

KILL -9 /*无条件强行终止进程*/

killall

与kill命令相似

格式:killall [-9] <进程名>

free

查看系统内存,交互空间的使用情况

格式:free  [-bkm]

free -b /*以Byte为单位显示信息*/

free -k /*以Kb为单位显示信息*/

free -m /*以Mb为单位显示信息*/

**************************环境相关*********************

echo

显示指定的字符串或变量的内容,不指定任何参数时将显示一个空行

read

从标准输入读入一个字符串并赋值给指定的变量

read -p /*在接受输入内容前显示提示字符串的内容,以增强交互性*/

export

将局部变量导出为全局变量或取消设置,允许在命令中直接为变量赋值

export -p /*查看当前命令环境中导出的全局变量

export -n /*取消全局变量的全局属性*/

exit

退出当前shell程序或命令环境,并返回一个退出状态的数字(0为正常,1为异常

********************************按名称查询文件******************************************
[root@emed4test /]# find / -name install.log
#####################查看当前线程################################
[root@jyyy ~]# ps -aux
5、查看进程情况
[root@localhost /]# ps -ef|grep rpc
********************************显示某一进程实时耗用的资源************************************************
top -p processId  显示某一进程实时耗用的资源
********************************显示包含该字符串的文件路径******************************************
[root@emed4test /]# locate install.html.
**********************************linux快捷键***************************
———————————————————–
1、将光标移到行首   Ctrl + a
2、将光标移到行尾   Ctrl + e
3、擦除光标前的整行 Ctrl + u
4、擦除光标后的整行 Ctrl + k
5、删除光标前的一个词 Ctrl + w  (以空格分隔)
6、清屏             Ctrl + l
7、杀掉当前进程     ctrl + c
8、重起             Ctrl + Alt + Del
*****************************************用vi修改文件**************************************
先启动shell:  bash
vi filename
切换模式                   i/esc    (修改模式显示insert,命令模式无显示)
输入命令冒号加行号数字     :1   即跳到第一行
跳到文件尾ctrl g
找某个关键字用             /关键字    按n向下找
输入:wq           保存修改并退出
apache的配置文件在conf/httpd.conf 通常修改影响压力测试的最大和最小进程数

***********************用户和组管*******************

useradd

添加系统用户

格式:useradd [选项] <用户名>

useradd -d /*制定用户的宿主目录*/

useradd -e /*指定用户的账号失效时间,可使用YYYY-MM-DD的日期格式*/

useradd -g /*指定用户的基本组名,也可以使用GID*/

useradd -G /*指定用户的公共组名,也可以使用GID*/

useradd -M /*不为用户建立并初始化宿主目录*/

useradd -s /*指定用户的登陆shell环境*/

useradd -u /*指定用户的UID号*/

passwd

设置系统用户密码,及锁定解锁用户帐户,若为指定用户,默认设置当前用户密码

格式:passwd [选项] <用户名>

passwd -d /*清空指定用户密码*/

passwd -l /*锁定指定用户账户*/

passwd -S /*查看指定用户状态*/

passwd -u /*解锁指定用户账户*/

usermod

修改指定用户帐户信息,大部分选项与useradd命令中的相同,不再赘余!可参考useradd选项设置

格式:usermod [选项] <用户名>

userdel

删除指定用户帐户

格式:userdel [-r] <用户名>

userdel -r /*删除用户后,也将该用户的宿主目录一并删除*/

groupadd

添加一个系统用户组

格式:groupadd [-g] <组名>

groupadd -g /*为新建的组指定GID组标记*/

groupdel

删除一个系统用户组

格式:groupdel  <组名>

su

切换为另一个用户身份,不指定参数时默认切换到root用户

格是:su [-l] [目标用户名]

su -l /*使用目标用户的登陆shell环境,该选项可简写为”-”

id

输出指定用户的身份标记信息,省略用户名参数时则输出当前用户的信息

格式:id [选项] <用户名>

id -u /*只显示有效用户信息*/

id -g /*只显示有效组信息*/

id -n /*只输出用户名称*/

 

 

/*************************查看日志常用*************************/

1、more命令2、cat命令3:tac命令,倒序显示4、head命令,可以指定显示那些内容5、tali命令,可以指定显示那些内容6、less 与 more 类似,但是比 more 更好的是,他可以[pg dn][pg up]翻页!

1、more 是我们最常用的工具之一,最常用的就是显示输出的内容,然后根据窗口的大小进行分页显示,然后还能提示文件的百分比;

# more /etc/profile

more 的语法、参数和命令;

more [参数选项] [文件]

参数如下:
+num                    从第num行开始显示;
-num                    定义屏幕大小,为num行;
+/pattern       从pattern 前两行开始显示;
-c                      从顶部清屏然后显示;
-d                      提示Press space to continue, ‘ q ‘ to quit.(按空 键继续,按q键退出),禁用响铃功能;
-l                      忽略Ctrl+l (换页)字符;
-p                      通过清除窗口而不是滚屏来对文件进行换页。和-c参数有点相似;
-s                              把连续的多个空行显示为一行;
-u                              把文件内容中的下划线去掉

退出more的动作指令是q

more 的参数应用举例;

# more -dc /etc/profile    注:显示提示,并从终端或控制台顶部显示;
# more + 4 /etc/profile      注:从profile的第4行开始显示;
# more – 4 /etc/profile      注:每屏显示4行;
# more +/MAIL /etc/profile     注:从profile中的第一个MAIL单词的前两行开始显示;

more 的动作指令;

我们查看一个内容较大的文件时,要用到more的动作指令,比如ctrl+f(或空格键)是向下显示一屏,ctrl+b是返回上一屏; Enter键可以向下滚动显示n行,要通过定,默认为1行;

我们只说几个常用的;自己尝试一下就知道了;

Enter         向下n行,需要定义,默认为1行;
Ctrl+f                  向下滚动一屏;
空 键                       向下滚动一屏;
Ctrl+b          返回上一屏;
=                               输出当前行的行号;
:f                      输出文件名和当前行的行号;
v                               调用vi编辑器;
! 命令                        调用Shell,并执行命令;
q                               退出more

当我们查看某一文件时,想调用vi来编辑它,不要忘记了v动作指令,这是比较方便的;

其它命令通过管道和more结合的运用例子;

比如我们列一个目录下的文件,由于内容太多,我们应该学会用more来分页显示。这得和管道 | 结合起来,比如:

# ls -l /etc  |more

2、cat命令

使用方式:cat [-AbeEnstTuv] [–help] [–version] fileName
说明:把档案串连接后传到基本输出(萤幕或加 > fileName 到另一个档案)
参数:
-n 或 –number 由 1 开始对所有输出的行数编号
-b 或 –number-nonblank 和 -n 相似,只不过对于空白行不编号
-s 或 –squeeze-blank 当遇到有连续两行以上的空白行,就代换为一行的空白行
-v 或 –show-nonprinting
范例:
cat -n textfile1 > textfile2 把 textfile1 的档案内容加上行号后输入 textfile2 这个档案里
cat -b textfile1 textfile2 >> textfile3 把 textfile1 和 textfile2 的档案内容加上行号(空白行不加)之后将内容附加到 textfile3 里。
范例:
把 textfile1 的档案内容加上行号后输入 textfile2 这个档案里
cat -n textfile1 > textfile2
把 textfile1 和 textfile2 的档案内容加上行号(空白行不加)之后将内容附加到 textfile3 里。
cat -b textfile1 textfile2 >> textfile3
cat /dev/ null > /etc/test.txt 此为清空/etc/test.txt档案内容
cat 也可以用来制作 image file。例如要制作软碟的 image file,将软碟放好后打
cat /dev/fd0 > OUTFILE

3:tac命令,倒序显示

tac: 从最后一行开始显示,可以看出 tac 是 cat 的反向显示!

4、head命令,可以指定显示那些内容

语法:

[root @test /root ]# head [-n number] [檔名]
参数说明:
-n :显示 number 行

说明:
head 的英文意思就是『头』啦,那么这个东东的用法自然就是显示出一个档案的前几行啰!没错!就是这样!若不加参数就默认输出前面十行内容,不信自己操作一下,也可以自定义输出的行数 那就加入『 head -n number filename 』即可!
比如我们显示/etc/profile的前10行内容,应该是:# head -n 10 /etc/profile

5、tali命令,可以指定显示那些内容

tail 是显示一个文件的内容的后多少行;

用法比较简单;

tail   -n  行数值  文件名;

比如我们显示/etc/profile的最后5行内容,应该是:

# tail  -n 5 /etc/profile

6、less 与 more 类似,但是比 more 更好的是,他可以[pg dn][pg up]翻页!

more ,less

其实这两个命令有极大的相似之处都是分页显示档案内容,但是区别也是有的,如下:

1)more:以百分比的形式分页显示,提示给用户已经显示了多少内容

less:没有百分比的提示

2)less更加灵活,可用通过【page down】【page up】上翻下翻页查看已经显示出的内容,而more不具备

3)对less显示出的内容中可以使用 / ‘ 字符 ‘ 输入需要查找的字符或者字符串并高亮显示,而more 不具备

3. nl

显示档案内容时输出行号,跟cat -n类似的功能,也是全盘输出

 

more & less 用 cat 的確可以顯示一個檔案的的內容﹐但如果檔案很長﹐超過一個熒幕怎麼辦﹖

您可以按著 Shift 鍵﹐再配合 PageUP 和 PageDown 鍵來回翻閱熒幕。但畢竟有限和不方便﹐您最好用 more 命令看一個檔案﹐這樣﹐結果只顯示一個熒幕的內容﹐然後您按 Enter 鍵的話﹐則往下捲動一行﹔如果按空白鍵的話﹐則往下捲動一個熒幕。同時﹐會在最底下告訴您目前所顯示的百分比。當檔案顯示到最後一行的時候﹐命令就結束。

不過﹐用 more 命令卻不能往上翻頁哦﹐如果您想要在顯示檔案的時候能夠上下來回翻頁﹐那就用 less 命令吧。這樣﹐您按 Enter 或向下方向鍵﹐則往下捲動一行﹔按空白鍵或 PageDown 則往下捲動一個熒幕﹔按向上方向鍵則往上捲動一行﹔按 PageUp 則往上捲動一個熒幕。但是﹐用 less 命令就算檔案顯示最後一行了﹐也不會自動結束﹐您得按 Q 鍵才能離開。

head & tail 好了﹐您用 more 或 less 可以逐頁翻開一個長檔案﹐但有時候您只想看檔案的前面數行﹐而不管後面的內容是什麼﹐那您可以用 head 這個命令。預設上﹐head 只會顯示一個檔案的前 10 行﹐但您可以用 -number 來指定顯示的行數﹕

head -20 /var/log/boot.log

那就可以看到這個檔案的前 20 行內容。但反過來﹐您只想看這個檔案的最後 20 行呢﹖用 tail 命令就是了﹕

tail -20 /var/log/boot.log

和 head 一樣﹐如果您不指定顯示行數﹐那麼 tail 也只顯示檔案的最後 10 行內容。不過﹐tail 還有一個很好用的參數﹐您可以用+number 來顯示第幾行起至檔案結尾的內容﹕

tail +2 /var/log/boot.log

那麼﹐除了第一行之外﹐第 2 行後面的內容都會顯示出來。其實﹐可用的選項很多啦﹐請您自己慢慢發掘囉。

tac 仔細點看﹐這個命令 cat 是調過來寫的﹐那麼它執行結果是否也調過來呢﹖猜對了﹗用 cat 命令可以顯示一個檔案內容﹐而且按每一行的排列順序的先後顯示。而這個 tac 呢﹐也是顯示檔案內容﹐但每一行的排列順序卻是調過來的。也就是﹐最後一行最先顯示﹐最先一行最後顯示。就醬子。

 

关机:

在linux下一些常用的关机/重启命令有shutdown、halt、reboot、及init,它们都可以达到重启系统的目的,但每个命令的内部工作过程是不同的。 1.shutdown
shutdown命令安全地将系统关机。 有些用户会使用直接断掉电源的方式来关闭linux,这是十分危险的。因为linux与windows不同,其后台运行着许多进程,所以强制关机可能会导致进程的数据丢失﹐使系统处于不稳定的状态﹐甚至在有的系统中会损坏硬件设备。

而在系统关机前使用shutdown命令﹐系统管理员会通知所有登录的用户系统将要关闭。并且login指令会被冻结﹐即新的用户不能再登录。直接关机或者延迟一定的时间才关机都是可能的﹐还可能重启。这是由所有进程〔process〕都会收到系统所送达的信号〔signal〕决定的。这让像vi之类的程序有时间储存目前正在编辑的文档﹐而像处理邮件〔mail〕和新闻〔news〕的程序则可以正常地离开等等。

shutdown执行它的工作是送信号〔signal〕给init程序﹐要求它改变runlevel。Runlevel 0被用来停机〔halt〕﹐runlevel 6是用来重新激活〔reboot〕系统﹐而runlevel 1则是被用来让系统进入管理工作可以进行的状态﹔这是预设的﹐假定没有-h也没有-r参数给shutdown。要想了解在停机〔halt〕或者重新开机〔reboot〕过程中做了哪些动作﹐你可以在这个文件/etc/inittab里看到这些runlevels相关的资料。

shutdown 参数说明:
[-t] 在改变到其它runlevel之前﹐告诉init多久以后关机。
[-r] 重启计算器。
[-k] 并不真正关机﹐只是送警告信号给每位登录者〔login〕。
[-h] 关机后关闭电源〔halt〕。
[-n] 不用init﹐而是自己来关机。不鼓励使用这个选项﹐而且该选项所产生的后果往往不总是你所预期得到的。
[-c] cancel current process取消目前正在执行的关机程序。所以这个选项当然没有时间参数﹐但是可以输入一个用来解释的讯息﹐而这信息将会送到每位使用者。
[-f] 在重启计算器〔reboot〕时忽略fsck。
[-F] 在重启计算器〔reboot〕时强迫fsck。
[-time] 设定关机〔shutdown〕前的时间。

2.halt—-最简单的关机命令

其实halt就是调用shutdown -h。halt执行时﹐杀死应用进程﹐执行sync系统调用﹐文件系统写操作完成后就会停止内核。
参数说明:
[-n] 防止sync系统调用﹐它用在用fsck修补根分区之后﹐以阻止内核用老版本的超级块〔superblock〕覆盖修补过的超级块。
[-w] 并不是真正的重启或关机﹐只是写wtmp〔/var/log/wtmp〕纪录。
[-d] 不写wtmp纪录〔已包含在选项[-n]中〕。
[-f] 没有调用shutdown而强制关机或重启。
[-i] 关机〔或重启〕前﹐关掉所有的网络接口。
[-p] 该选项为缺省选项。就是关机时调用poweroff。

3.reboot
reboot的工作过程差不多跟halt一样﹐不过它是引发主机重启﹐而halt是关机。它的参数与halt相差不多。

4.init
init是所有进程的祖先﹐它的进程号始终为1﹐所以发送TERM信号给init会终止所有的用户进程﹑守护进程等。shutdown 就是使用这种机制。init定义了8个运行级别(runlevel), init 0为关机﹐init 1为重启。关于init可以长篇大论﹐这里就不再叙述。另外还有 telinit命令可以改变init的运行级别﹐比如﹐telinit -iS可使系统进入单用户模式﹐并且得不到使用shutdown时的信息和等待时间。

java内存管理及调用函数参数的传递机制

用来保存变量的值和类的实例(堆区对象的引用(指针),Person p = new Person(),p放在栈上,p是个引用(指针),new出来的对象放在堆上),常量池也放在上。【栈保存变量和引用,堆保存new出来的实例和常量池

class Test
{
public static void main (String[] args) throws java.lang.Exception
{
Test test = new Test();
int a = 10;
test.setNum(a);
System.out.println(a);
}
public void setNum(int i){
i = i+1000;
}
}

对于函数调用,只有传进去一个对象,在函数里面对这个对象执行set方法时,这个对象的值才会变,所以上面例子依旧输出10。

Java程序运行时内存分配,主要是两种类型的变量:基本类型引用类型。二者作为局部变量,都放在中,基本类型直接在栈中保存值,引用类型只保存一个指向堆区的指针,真正的对象在里。作为参数时基本类型就直接传值,引用类型传指针。

  • 基本类型有:byte、short、char、int、long、boolean。
  • 基本类型的包装类分别是:Byte、Short、Character、Integer、Long、Boolean。

上边这些包装类都实现了常量池技术,另外两种浮点数类型的包装类则没有实现。String类型也实现了常量池技术。(实现常量池技术的)

栈中的数据可以共享,所以基本类型的变量保存在栈上,当重新定义一个值相同的变量时,不会再申请一块空间,而是直接指向那个变量。比如定义了int i = 40;,再定义int i0 = 40;这时候会自动检查栈中是否有40这个数据,如果有,i0会直接指向i的40,不会再添加一个新的40。

Integer是包装类。由于Integer 包装类实现了常量池技术,因此new Integer(40)的40均是从常量池中获取的,均指向同一个地址。但是仅限[-128至127]这个范围内的常量,如果常量值超过这个范围,就会从堆中创建对象,不再从常量池中取,那时候new两个变量值就不会相等了。

今天学习Python,提到python是通过赋值传递的。然后联想到Java,java传递参数是通过什么方式呢?是值传递还是引用传递?

在网上查阅资料知:java传递参数只有值传递。当参数为普通类型时,传递的是值;当参数为引用类型时,传递的是对象的引用(即地址),都是复制的值。在开发中也遇到过类似的问题:最常见的翻页问题:前台传过来一个Page对象,我们后台通过调用函数(参数中含有Page),在函数内部对page进行处理,设置Page的页数,及行数,当处理完后,函数调用结束,我们不必再将Page返回,因为此时前台传来的page对象中的值已经改变了。    其中就是因为java的值传递原理。

这里面对java值传递讲解的非常清楚:(真是一篇好文章)

  http://www.cnblogs.com/lixiaolun/p/4311775.html

捎带看了下面一片文章,又了解了一些JVM内存放面的设计:对象池。  java的基本类型的封装类有对象池的设计,如Integer类型:有cache,在-128-127范围内有赋值时,会先从对象池中取数据,此时得到的是同一个对象的引用,对象地址是相同的。当超过这个范围时,则重新创建对象。

http://www.cnblogs.com/DreamSea/archive/2011/11/20/2256396.html

在大神的博文:http://blog.csdn.NET/yangyuankp/article/details/7651251 的基础上加以修改。

本文将由浅入深详细介绍Java内存分配的原理,以帮助新手更轻松的学习Java。这类文章网上有很多,但大多比较零碎。本文从认知过程角度出发,将带给读者一个系统的介绍。

进入正题前首先要知道的是Java程序运行在JVM(Java  Virtual Machine,Java虚拟机)上,可以把JVM理解成Java程序和操作系统之间的桥梁,JVM实现了Java的平台无关性,由此可见JVM的重要性。

*.java文件首先使用javac编译成*.class文件,*.class文件是与平台无关的字节码。只要在不同的平台上实现相应的虚拟机,编译后的字节码*.class文件就可以在该平台上运行了。这是java跨平台的关键。

JVM是一个抽象的计算机,和实际的计算机一样,也有自己的指令集并使用不同的存储区域。它负责执行指令,管理数据、内存和寄存器。所以在学习Java内存分配原理的时候一定要牢记这一切都是在JVM中进行的,JVM是内存分配原理的基础与前提。

 

简单通俗的讲,一个完整的Java程序运行过程会涉及以下内存区域:

寄存器:JVM内部虚拟寄存器,存取速度非常快,程序不可控制。

栈:保存局部变量的值,包括:1.用来保存基本数据类型的值;2.保存类的实例,即堆区对象的引用(指针)。也可以用来保存加载方法时的帧。

堆:用来存放动态产生的数据,比如new出来的对象。注意创建出来的对象只包含属于各自的成员变量,并不包括成员方法。因为同一个类的对象拥有各自的成员变量,存储在各自的堆中,但是他们共享该类的方法,并不是每创建一个对象就把成员方法复制一次。

假设有个Person类。代码:Person p = new Person();创建了一个Person对象,并把Person对象赋给p变量。这段代码产生了2个东西:

1是变量p。存放在栈中,如下图左侧。(引用类型变量)

2是Person对象。存放在堆中,如下图右侧。

常量池:JVM为每个已加载的类型维护一个常量池,常量池就是这个类型用到的常量的一个有序集合。包括直接常量(基本类型,String)和对其他类型、方法、字段的符号引用(1)。池中的数据和数组一样通过索引访问。由于常量池包含了一个类型所有的对其他类型、方法、字段的符号引用,所以常量池在Java的动态链接中起了核心作用。常量池存在于堆中。

代码段:用来存放从硬盘上读取的源程序代码。

数据段:用来存放static定义的静态成员。

下面是内存表示图:

 

上图中大致描述了Java内存分配,接下来通过实例详细讲解Java程序是如何在内存中运行的(注:以下图片引用自尚学堂马士兵老师的J2SE课件,图右侧是程序代码,左侧是内存分配示意图,我会一一加上注释)。

预备知识:

1.一个Java文件,只要有main入口方法,我们就认为这是一个Java程序,可以单独编译运行。

2.无论是普通类型的变量还是引用类型的变量(俗称实例),都可以作为局部变量,他们都可以出现在栈中。只不过普通类型的变量在栈中直接保存它所对应的值,而引用类型的变量保存的是一个指向堆区的指针,通过这个指针,就可以找到这个实例在堆区对应的对象。因此,普通类型变量只在栈区占用一块内存,而引用类型变量要在栈区和堆区各占一块内存。

示例:

 

1.JVM自动寻找main方法,执行第一句代码,创建一个Test类的实例,在栈中分配一块内存,存放一个指向堆区对象的指针110925。

2.创建一个int型的变量date,由于是基本类型,直接在栈中存放date对应的值9。

3.创建两个BirthDate类的实例d1、d2,在栈中分别存放了对应的指针指向各自的对象(对象在堆中)。他们在实例化时调用了有参数的构造方法,因此对象中有自定义初始值。

调用test对象的change1方法,并且以date为参数。JVM读到这段代码时,检测到i是局部变量,因此会把i放在栈中,并且把date的值赋给i。

把1234赋给i。很简单的一步。

change1方法执行完毕,立即释放局部变量i所占用的栈空间。

         调用test对象的change2方法,以实例d1为参数。JVM检测到change2方法中的b参数为局部变量,立即加入到栈中,由于是引用类型的变量,所以b中保存的是d1中的指针,此时b和d1指向同一个堆中的对象。在b和d1之间传递是指针。

         change2方法中又实例化了一个BirthDate对象,并且赋给b。在内部执行过程是:在堆区new了一个对象,并且把该对象的指针保存在栈中的b对应空间,此时实例b不再指向实例d1所指向的对象,但是实例d1所指向的对象并无变化,这样无法对d1造成任何影响。

         change2方法执行完毕,立即释放局部引用变量b所占的栈空间,注意只是释放了栈空间,堆空间要等待自动回收。

         调用test实例的change3方法,以实例d2为参数。同理,JVM会在栈中为局部引用变量b分配空间,并且把d2中的指针存放在b中,此时d2和b指向同一个对象。再调用实例b的setDay方法,其实就是调用d2指向的对象的setDay方法。

         调用实例b的setDay方法会影响d2,因为二者指向的是同一个对象。

         change3方法执行完毕,立即释放局部引用变量b。

以上就是Java程序运行时内存分配的大致情况。其实也没什么,掌握了思想就很简单了。无非就是两种类型的变量:基本类型和引用类型。二者作为局部变量,都放在栈中,基本类型直接在栈中保存值,引用类型只保存一个指向堆区的指针,真正的对象在堆里。作为参数时基本类型就直接传值,引用类型传指针。

小结:

1.分清什么是实例什么是对象。Class a= new Class();此时a叫实例,而不能说a是对象(new Class()才是对象)。实例在栈中,对象在堆中,操作实例实际上是通过实例的指针间接操作对象。多个实例可以指向同一个对象。

2.栈中的数据和堆中的数据销毁并不是同步的。方法一旦结束,栈中的局部变量立即销毁,但是堆中对象不一定销毁。因为可能有其他变量也指向了这个对象,直到栈中没有变量指向堆中的对象时,它才销毁,而且还不是马上销毁,要等垃圾回收扫描时才可以被销毁。

3.以上的栈、堆、代码段、数据段等等都是相对于应用程序而言的。每一个应用程序都对应唯一的一个JVM实例,每一个JVM实例都有自己的内存区域,互不影响。并且这些内存区域是所有线程共享的。这里提到的栈和堆都是整体上的概念,这些堆栈还可以细分。

4.类的成员变量在不同对象中各不相同,都有自己的存储空间(成员变量在堆中的对象中)。而类的方法却是该类的所有对象共享的,只有一套,对象使用方法的时候方法才被压入栈,方法不使用则不占用内存。

以上分析只涉及了栈和堆,还有一个非常重要的内存区域:常量池,这个地方往往出现一些莫名其妙的问题。常量池是干嘛的上边已经说明了,也没必要理解多么深刻,只要记住它维护了一个已加载类的常量就可以了。接下来结合一些例子说明常量池的特性。

预备知识:

基本类型和基本类型的包装类。

基本类型有:byte、short、char、int、long、boolean。

基本类型的包装类分别是:Byte、Short、Character、Integer、Long、Boolean。

注意区分大小写。

二者的区别是:基本类型体现在程序中是普通变量,基本类型的包装类是类,体现在程序中是引用变量。

因此二者在内存中的存储位置不同:基本类型存储在栈中,而基本类型包装类存储在堆中。上边提到的这些包装类都实现了常量池技术,另外两种浮点数类型的包装类则没有实现。另外,String类型也实现了常量池技术。

实例:

 

结果:

1
2
3
4
5
6
7
i=i0       true
i1=i2      true
i1=i2+i3   true
i4=i5      <span style="color: #ff0000;">false</span>
i4=i5+i6   true
d1=d2      <span style="color: #ff0000;">false
</span>

结果分析:

1.i和i0均是普通类型(int)的变量,所以数据直接存储在栈中,而栈有一个很重要的特性:栈中的数据可以共享。当我们定义了int i = 40;,再定义int i0 = 40;这时候会自动检查栈中是否有40这个数据,如果有,i0会直接指向i的40,不会再添加一个新的40。

2.i1和i2均是引用类型,在栈中存储指针,因为Integer是包装类。由于Integer 包装类实现了常量池技术,因此i1、i2的40均是从常量池中获取的,均指向同一个地址,因此i1=12。

3.很明显这是一个加法运算,Java的数学运算都是在栈中进行的,Java会自动对i1、i2进行拆箱操作转化成整型,因此i1在数值上等于i2+i3。

4.i4和i5 均是引用类型,在栈中存储指针,因为Integer是包装类。但是由于他们各自都是new出来的,因此不再从常量池寻找数据,而是从堆中各自new一个对象,然后各自保存指向对象的指针,所以i4和i5不相等,因为他们所存指针不同,所指向对象不同。

5.这也是一个加法运算,和3同理。

6.d1和d2均是引用类型,在栈中存储指针,因为Double是包装类。但Double包装类没有实现常量池技术,因此Doubled1=1.0;相当于Double d1=new Double(1.0);,是从堆new一个对象,d2同理。因此d1和d2存放的指针不同,指向的对象不同,所以不相等。

 

小结:

1.以上提到的几种基本类型包装类均实现了常量池技术,但他们维护的常量仅仅是【-128至127】这个范围内的常量,如果常量值超过这个范围,就会从堆中创建对象,不再从常量池中取。比如,把上边例子改成Integer i1 = 400; Integer i2 = 400;,很明显超过了127,无法从常量池获取常量,就要从堆中new新的Integer对象,这时i1和i2就不相等了。

2.String类型也实现了常量池技术,但是稍微有点不同。String型是先检测常量池中有没有对应字符串,如果有,则取出来;如果没有,则把当前的添加进去。

凡是涉及内存原理,一般都是博大精深的领域,切勿听信一家之言,多读些文章。我在这只是浅析,里边还有很多猫腻,就留给读者探索思考了。希望本文能对大家有所帮助!

 

脚注:

(1) 符号引用,顾名思义,就是一个符号,符号引用被使用的时候,才会解析这个符号。如果熟悉Linux或unix系统的,可以把这个符号引用看作一个文件的软链接,当使用这个软连接的时候,才会真正解析它,展开它找到实际的文件对于符号引用,在类加载层面上讨论比较多,源码级别只是一个形式上的讨论。

当一个类被加载时,该类所用到的别的类的符号引用都会保存在常量池,实际代码执行的时候,首次遇到某个别的类时,JVM会对常量池的该类的符号引用展开,转为直接引用,这样下次再遇到同样的类型时,JVM就不再解析,而直接使用这个已经被解析过的直接引用。

除了上述的类加载过程的符号引用说法,对于源码级别来说,就是依照引用的解析过程来区别代码中某些数据属于符号引用还是直接引用,如,System.out.println(“test” +”abc”);//这里发生的效果相当于直接引用,而假设某个Strings = “abc”; System.out.println(“test” + s);//这里的发生的效果相当于符号引用,即把s展开解析,也就相当于s是”abc”的一个符号链接,也就是说在编译的时候,class文件并没有直接展看s,而把这个s看作一个符号,在实际的代码执行时,才会展开这个。

 

参考文章:

java内存分配研究:http://www.blogjava.net/Jack2007/archive/2008/05/21/202018.html

Java常量池详解之一道比较蛋疼的面试题:http://www.cnblogs.com/DreamSea/archive/2011/11/20/2256396.html

jvm常量池:http://www.cnblogs.com/wenfeng762/archive/2011/08/14/2137820.html

深入Java核心 Java内存分配原理精讲:http://developer.51cto.com/art/201009/225071.htm

Java线上应用故障排查之二:高内存占用

前一篇介绍了线上应用故障排查之一:高CPU占用,这篇主要分析高内存占用故障的排查。

Java开发的,经常会碰到下面两种异常:

1、java.lang.OutOfMemoryError: PermGen space

2、java.lang.OutOfMemoryError: Java heap space

要详细解释这两种异常,需要简单重提下Java内存模型。

(友情提示:本博文章欢迎转载,但请注明出处:hankchen,http://www.blogjava.net/hankchen

Java内存模型是描述Java程序中各变量(实例域、静态域和数组元素)之间的关系,以及在实际计算机系统中将变量存储到内存和从内存取出变量这样的低层细节。

在Java虚拟机中,内存分为三个代:新生代(New)、老生代(Old)、永久代(Perm)。

(1)新生代New:新建的对象都存放这里

(2)老生代Old:存放从新生代New中迁移过来的生命周期较久的对象。新生代New和老生代Old共同组成了堆内存。

(3)永久代Perm:是非堆内存的组成部分。主要存放加载的Class类级对象如class本身,method,field等等。

如果出现java.lang.OutOfMemoryError: Java heap space异常,说明Java虚拟机的堆内存不够。原因有二:

(1)Java虚拟机的堆内存设置不够,可以通过参数-Xms、-Xmx来调整。

(2)代码中创建了大量大对象,并且长时间不能被垃圾收集器收集(存在被引用)。

如果出现java.lang.OutOfMemoryError: PermGen space,说明是Java虚拟机对永久代Perm内存设置不够。

一般出现这种情况,都是程序启动需要加载大量的第三方jar包。例如:在一个Tomcat下部署了太多的应用。

 

从代码的角度,软件开发人员主要关注java.lang.OutOfMemoryError: Java heap space异常,减少不必要的对象创建,同时避免内存泄漏。

现在以一个实际的例子分析内存占用的故障排查。

2G19({7(0}N(FIL09LH175N

通过top命令,发现PID为9004的Java进程一直占用比较高的内存不释放(24.7%),出现高内存占用的故障。

想起上一篇线上应用故障排查之一:高CPU占用介绍的PS命令,能否找到具体是哪个的线程呢?

ps -mp 9004 -o THREAD,tid,time,rss,size,%mem

1

遗憾的是,发现PS命令可以查到具体进程的CPU占用情况,但是不能查到一个进程下具体线程的内存占用情况。

 

只好寻求其他方法了,幸好Java提供了一个很好的内存监控工具:jmap命令

jmap命令有下面几种常用的用法:

•jmap [pid]

•jmap -histo:live [pid] >a.log

•jmap -dump:live,format=b,file=xxx.xxx [pid]

用得最多是后面两个。其中,jmap -histo:live [pid] 可以查看当前Java进程创建的活跃对象数目和占用内存大小。

jmap -dump:live,format=b,file=xxx.xxx [pid] 则可以将当前Java进程的内存占用情况导出来,方便用专门的内存分析工具(例如:MAT)来分析。

这个命令对于分析是否有内存泄漏很有帮助。具体怎么使用可以查看本博的另一篇文章:利用Eclipse Memory Analyzer Tool(MAT)分析内存泄漏

 

这里详细介绍下jmap -histo:live [pid] 命令:

1

从上图可以看出,int数组、constMethodKlass、methodKlass、constantPoolKlass都占用了大量的内存。

特别是占用了大量内存的int数组,需要仔细检查相关代码。

其中:

[C is a char[]
[S is a short[]
[I is a int[]
[B is a byte[]
[[I is a int[][]

[C对象占用Heap多,往往跟String有关,String其内部使用final char[]数组来保存数据的。

constMethodKlass/ methodKlass/ constantPoolKlass/ constantPoolCacheKlass/ instanceKlassKlass/ methodDataKlass

与Classloader相关,常驻与Perm区。

最后,总结下排查内存故障的方法和技巧有哪些:

1、top命令:Linux命令。可以查看实时的内存使用情况。

2、jmap -histo:live [pid],然后分析具体的对象数目和占用内存大小,从而定位代码。

3、jmap -dump:live,format=b,file=xxx.xxx [pid],然后利用MAT工具分析是否存在内存泄漏等等。

(友情提示:本博文章欢迎转载,但请注明出处:hankchen,http://www.blogjava.net/hankchen

线上应用故障排查之一:高CPU占用

一个应用占用CPU很高,除了确实是计算密集型应用之外,通常原因都是出现了死循环。

(友情提示:本博文章欢迎转载,但请注明出处:hankchen,http://www.blogjava.net/hankchen

以我们最近出现的一个实际故障为例,介绍怎么定位和解决这类问题。

clip_image002

根据top命令,发现PID为28555的Java进程占用CPU高达200%,出现故障。

通过ps aux | grep PID命令,可以进一步确定是tomcat进程出现了问题。但是,怎么定位到具体线程或者代码呢?

首先显示线程列表:

ps -mp pid -o THREAD,tid,time

1

找到了耗时最高的线程28802,占用CPU时间快两个小时了!

其次将需要的线程ID转换为16进制格式:

printf “%x\n” tid

2

最后打印线程的堆栈信息:

jstack pid |grep tid -A 30

3

找到出现问题的代码了!

现在来分析下具体的代码:ShortSocketIO.readBytes(ShortSocketIO.java:106)

ShortSocketIO是应用封装的一个用短连接Socket通信的工具类。readBytes函数的代码如下:

public byte[] readBytes(int length) throws IOException {

if ((this.socket == null) || (!this.socket.isConnected())) {

throw new IOException(“++++ attempting to read from closed socket”);

}

byte[] result = null;

ByteArrayOutputStream bos = new ByteArrayOutputStream();

if (this.recIndex >= length) {

bos.write(this.recBuf, 0, length);

byte[] newBuf = new byte[this.recBufSize];

if (this.recIndex > length) {

System.arraycopy(this.recBuf, length, newBuf, 0, this.recIndex – length);

}

this.recBuf = newBuf;

this.recIndex -= length;

} else {

int totalread = length;

if (this.recIndex > 0) {

totalread -= this.recIndex;

bos.write(this.recBuf, 0, this.recIndex);

this.recBuf = new byte[this.recBufSize];

this.recIndex = 0;

}

int readCount = 0;

while (totalread > 0) {

if ((readCount = this.in.read(this.recBuf)) > 0) {

if (totalread > readCount) {

bos.write(this.recBuf, 0, readCount);

this.recBuf = new byte[this.recBufSize];

this.recIndex = 0;

} else {

bos.write(this.recBuf, 0, totalread);

byte[] newBuf = new byte[this.recBufSize];

System.arraycopy(this.recBuf, totalread, newBuf, 0, readCount – totalread);

this.recBuf = newBuf;

this.recIndex = (readCount – totalread);

}

totalread -= readCount;

}

}

}

问题就出在标红的代码部分。如果this.in.read()返回的数据小于等于0时,循环就一直进行下去了。而这种情况在网络拥塞的时候是可能发生的。

至于具体怎么修改就看业务逻辑应该怎么对待这种特殊情况了。

 

最后,总结下排查CPU故障的方法和技巧有哪些:

1、top命令:Linux命令。可以查看实时的CPU使用情况。也可以查看最近一段时间的CPU使用情况。

2、PS命令:Linux命令。强大的进程状态监控命令。可以查看进程以及进程中线程的当前CPU使用情况。属于当前状态的采样数据。

3、jstack:Java提供的命令。可以查看某个进程的当前线程栈运行情况。根据这个命令的输出可以定位某个进程的所有线程的当前运行状态、运行代码,以及是否死锁等等。

4、pstack:Linux命令。可以查看某个进程的当前线程栈运行情况。

(友情提示:本博文章欢迎转载,但请注明出处:hankchen,http://www.blogjava.net/hankchen

mongostat

  • inserts/s 每秒插入次数
  • query/s 每秒查询次数

注:10条简单的查询可能比一条复杂的查询速度还快, 所以数值的大小,意义并不大。

  • update/s 每秒更新次数
  • delete/s 每秒删除次数
  • getmore/s查询时游标(cursor)的getmore操作,每秒执行getmore次数
  • command/s 每秒的命令数,比以上插入、查找、更新、删除的综合还多,还统计了别的命令,在主从系统中,会显示两个值 (例如:80|0),分别代表 本地|复制 命令的个数
  • flushs/s 每秒执行fsync将数据写入硬盘的次数。

注:一般都是0,或者1,通过计算两个1之间的间隔时间,可以大致了解多长时间flush一次。flush开销是很大的,如果频繁的flush,可能就要找找原因了。

  • mapped/s 所有的被mmap的数据量,单位是MB,
  • vsize 虚拟内存使用量,单位MB
  • res 物理内存使用量,单位MB

注:mapped, vsize一般不会有大的变动, res会慢慢的上升,如果res经常突然下降,去查查是否有别的程序狂吃内存。

  • faults/s 每秒访问失败数(只有Linux有),数据被交换出物理内存,放到swap。不要超过100,否则就是机器内存太小,造成频繁swap写入。此时要升级内存或者扩展,大压力下这个数值往往不为0。如果经常不为0,那就该加内存了。
  • locked % 被锁的时间百分比,尽量控制在50%以下吧
  • idx miss % 索引不命中所占百分比。如果太高的话就要考虑索引是不是少了
  • qr  客户端等待从 MongoDB 实例读取数据的队列长度。
  • qw  客户端等待向 MongoDB 实例写入数据的队列长度。
  • ar  执行读取操作的活动客户端的数目。
  • aw  执行写入操作的活动客户端的数目。
  • q t|r|w 当Mongodb接收到太多的命令而数据库被锁住无法执行完成,它会将命令加入队列。这一栏显示了总共、读、写3个队列的长度,都为0的话表示mongo毫无压力。高并发时,一般队列值会升高。
  • netIn
  • netOut 网络带宽压力
  • conn 当前连接数

注: MongoDB为每一个连接创建一个线程,线程的创建和释放也是有开销的。尽量不要让这个数值很大

  • time 时间戳

Web通信

为达到实时通信,一类是基于HTTP的Comet推送技术,另一类是基于套接口(Socket)传送信息实现消息传输。

一、目前使用Comet主要有两种方式,轮询和iframe流。

  • 轮询 polling
    浏览器周期性的发出请求,如果服务器没有新数据需要发送就返回以空响应。这种方法问题很大:首先,大量无意义的请求造成网络压力;其次,请求周期的限制不能及时地获得最新数据。这种方法很快就被淘汰。
  • 长轮询 long polling
    长轮询是在打开一条连接以后保持连接,等待服务器推送来数据再关闭连接。然后浏览器再发出新的请求,这能更好地管理请求数量,也能及时地更新数据。AJAX调用XMLHttpRequest对象发出HTTP请求,JS响应处理函数根据服务器返回的数据更新HTML页面的展示。这个方法一定程度上消除了简单轮询的弊端,但服务器压力也是很大。
  • iframe流 iframe streaming
    iframe流方式是在页面中插入一个隐藏的iframe,利用其src属性在服务器和客户端之间建立一条长链接,服务器向iframe传输数据(通常是HTML,内有负责插入信息的javascript),来实时更新页面。”iframe是很早就存在的一种 HTML 标记,通过在 HTML 页面里嵌入一个隐蔵帧,然后将这个隐蔵帧的 SRC属性设为对一个长连接的请求,服务器端就能源源不断地往客户端输入数据。”其不足为:进度条会显示一直,反应在页面上就是浏览器标签页的图标会不停地转动。(当然这也是有解决方法的)

    二、基于WebSocket

    HTML5提供的Websocket不同于上面这些在老的HTML已有框架内的方法,而是在单个TCP连接上进行全双工通讯的协议。目前主流浏览器都已支持。
    1. 初始化过程
    不同于早期JAVA使用在浏览器安装插件的方法——-Java Applet 套接口:这种方法不足在于Java Applet再收到服务器返回的消息后,无法通过Javascript去更新HTML页面的内容。而是通过HTTP建立连接(HTTP handshake)。
    2. 开始通讯
    一旦初始连接建立,浏览器和服务器就打开了一个TCP socket的频道。在这个频道内就能进行双向的数据通信。

    然而Websocket依然有一些问题。比如浏览器兼容性问题(随着浏览器的发展,肯定是越来越小的),以及网络中间物(代理服务、防火墙)问题不支持WebSocket,这时Socket.io的出现就是为了完善WebSocket。

  • Socket.IO

    Guillermo Rauch在2010年开发第一版时,目的很明确地指向Node.js实时应用。在几次版本更新后,重新定义和封装核心功能而分化出一个基础模块 Engine.io——力求建立更稳定的工具。Engine.IO有着更稳定的连接质量。使得Socket.IO在先打开一个长轮询,再在将连接推至WebSocket频道继续通信。
    在使用Node的http模块创建服务器同时还要Express应用,因为这个服务器对象需要同时充当Express服务和Socket.io服务。

java修饰符static和final

static表示不要实例化就可以使用,静态变量只在加载类的过程中分配一次内存,没有被static修饰的实例变量,在每次实例化时都分配一次内存。

static final用来修饰成员变量和成员方法,可简单理解为“全局常量”!
对于变量,表示一旦给值就不可修改,并且通过类名可以访问。
对于方法,表示不可覆盖,并且可以通过类名直接访问。

修饰符
名称 说明 备注
static 静态变量(又称为类变量,其它的称为实例变量) 可以被类的所有实例共享。并不需要创建类的实例就可以访问静态变量
final 常量,值只能够分配一次,不能更改 注意不要使用const,虽然它和C、C++中的const关键字含义一样可以同static一起使用,避免对类的每个实例维护一个拷贝
transient 告诉编译器,在类对象序列化的时候,此变量不需要持久保存 主要是因为改变量可以通过其它变量来得到,使用它是为了性能的问题
volatile 指出可能有多个线程修改此变量,要求编译器优化以保证对此变量的修改能够被正确的处理

final类不能被继承,没有子类,final类中的方法默认是final的。
final方法不能被子类的方法覆盖,但可以被继承。
final成员变量表示常量,只能被赋值一次,赋值后值不再改变。
final不能用于修饰构造方法。
注意:父类的private成员方法是不能被子类方法覆盖的,因此private类型的方法默认是final类型的。

static变量前可以有private修饰,表示这个变量可以在类的静态代码块中,或者类的其他静态成员方法中使用(当然也可以在非静态成员方法中使用–废话),但是不能在其他类中通过类名来直接引用,这一点很重要。private是访问权限限定,static表示不要实例化就可以使用。

 

1、static变量

        静态变量
        按照是否静态的对类成员变量进行分类可分两种:一种是被static修饰的变量,叫静态变量或类变量;另一种是没有被static修饰的变量,叫实例变量。两者的区别是:
对于静态变量在内存中只有一个拷贝(节省内存),JVM只为静态分配一次内存,在加载类的过程中完成静态变量的内存分配,可用类名直接访问(方便),当然也可以通过对象来访问(但是这是不推荐的)。
对于实例变量,没创建一个实例,就会为实例变量分配一次内存,实例变量可以在内存中有多个拷贝,互不影响(灵活)。

2、静态方法
        静态方法可以直接通过类名调用,任何的实例也都可以调用,因此静态方法中不能用this和super关键字,不能直接访问所属类的实例变量和实例方法(就是不带static的成员变量和成员成员方法),只能访问所属类的静态成员变量和成员方法。因为实例成员与特定的对象关联!这个需要去理解,想明白其中的道理,不是记忆!!!
因为static方法独立于任何实例,因此static方法必须被实现,而不能是抽象的abstract。
3、static代码块
static代码块也叫静态代码块,是在类中独立于类成员的static语句块,可以有多个,位置可以随便放,它不在任何的方法体内,JVM加载类时会执行这些静态的代码块,如果static代码块有多个,JVM将按照它们在类中出现的先后顺序依次执行它们,每个代码块只会被执行一次。例如:
public class Test5 {
private static int a;
private int b;static {
Test5.a = 3;
System.out.println(a);
Test5 t = new Test5();
t.f();
t.b = 1000;
System.out.println(t.b);
}static {
Test5.a = 4;
System.out.println(a);
}public static void main(String[] args) {
// TODO 自动生成方法存根
}

static {
Test5.a = 5;
System.out.println(a);
}

public void f() {
System.out.println(“hhahhahah”);
}
}

运行结果:
3
hhahhahah
1000
4
5
        利用静态代码块可以对一些static变量进行赋值,最后再看一眼这些例子,都一个static的main方法,这样JVM在运行main方法的时候可以直接调用而不用创建实例。