Hash(散列)

  • 数组与链表数据结构存储数据的方式:
因为数组删除、插入操作时,同时需要操作后面的元素,所以增、删效率较低,而链表的结构导致了查找的效率较低,我们是否可以构造一个关系,使得待存储的集合{a,b,c,d,e,f}中的任意一个元素通过这个关系都能找到一个指定的索引值,所以现在假设有一种方法f,可以将各个元素和存储位置对应起来,那么这个方法f就是hash函数(散列函数)。

根据上图我们知道原有集合中的元素被散列之后所得到的有序对的集合为{(d,0),(b,2),(c,2),(a,3),(f,4),(e,5)}。其中,散列表中的1号槽为空,表示经过散列函数作用之后,原集合中没有任何元素被散列至该位置,而2号槽中则存在两个元素,将两个不同元素散列至相同位置的情形我们称为碰撞(Collision)。【hashMap中碰撞发生时,用链表进行存储,存储结构中包含key和value,当进行查找时,先查找hashCode找到对应槽(桶?bucket?),然后在链表中根据key进行查找value】

 

  • 常用的散列函数:
    1、除余法
    顾名思义,除余法就是用关键码x除以M(往往取散列表长度),并取余数作为散列地址。除余法几乎是最简单的散列方法,散列函数为: h(x) = x mod M。

 

 

  • 冲突解决的策略

尽管散列函数的目标是使得冲突最少,但实际上冲突是无法避免的。因此,我们必须研究冲突解决策略。冲突解决技术可以分为两类:开散列方法( open hashing,也称为拉链法,separate chaining )和闭散列方法( closed hashing,也称为开地址方法,open addressing )。这两种方法的不同之处在于:开散列法把发生冲突的关键码存储在散列表主表之外,而闭散列法把发生冲突的关键码存储在表中另一个槽内。

开散列方法:

1、拉链法

开散列方法的一种简单形式是把散列表中的每个槽定义为一个链表的表头。散列到一个特定槽的所有记录都放到这个槽的链表中。图9-5说明了一个开散列的散列表,这个表中每一个槽存储一个记录和一个指向链表其余部分的指针。这7个数存储在有11个槽的散列表中,使用的散列函数是h(K) = K mod 11。数的插入顺序是77、7、110、95、14、75和62。有2个值散列到第0个槽,1个值散列到第3个槽,3个值散列到第7个槽,1个值散列到第9个槽。

 

哈希表有多种不同的实现方法,最常用的一种方法就是 拉链法,可以理解为“链表的数组” ,如图:

从上图我们可以发现哈希表是由数组+链表组成的,一个长度为16的数组中,每个元素存储的是一个链表的头结点。那么这些元素是按照什么样的规则存储到数组中呢。一般情况是通过hash(key)%len获得,也就是元素的key的哈希值对数组长度取模得到。比如上述哈希表中,12%16=12,28%16=12,108%16=12,140%16=12。所以12、28、108以及140都存储在数组下标为12的位置。

HashMap其实也是一个线性的数组实现的,所以可以理解为其存储数据的容器就是一个线性数组。这可能让我们很不解,一个线性的数组怎么实现按键值对来存取数据呢?这里HashMap有做一些处理。

首先HashMap里面实现一个静态内部类Entry,其重要的属性有 key , value, next,从属性key,value我们就能很明显的看出来Entry就是HashMap键值对实现的一个基础bean,我们上面说到HashMap的基础就是一个线性数组,这个数组就是Entry[],Map里面的内容都保存在Entry[]里面。

 

2、桶式散列

桶式散列方法的基本思想是把一个文件的记录分为若干存储桶,每个存储桶包含一个或多个页块,一个存储桶内的各页块用指针连接起来,每个页块包含若干记录。散列函数h把关键码值K转换为存储桶号,即h(K)表示具有关键码值K的记录所在的存储桶号。 图9-6表示了一个具有B个存储桶的散列文件组织。有一个存储桶目录表,存放B个指针,每个存储桶一个,每个指针就是所对应存储桶的第一个页块的地址。

有些存储桶仅仅由一个页块组成,如下图中的1号存储桶。有的存储桶由多个页块组成,每一个页块的块头上有一个指向下一个页块的指针,例如,如下图中的第B-1号存储桶由b4,b5,b6三个页块组成,每个存储桶中最后一个页块的头上为空指针。

 

摘录自:http://blog.csdn.net/koself/article/details/7869453

http://blog.csdn.net/jn1158359135/article/details/7205688

http://blog.csdn.net/vking_wang/article/details/14166593

equals和==

  • ==用来比较两个对象的引用(地址)是否相同,用来比较基本数据类型时,用来比较值是否相等。
  • equals用来比较两个引用的对象的值是否相同。

 

  • 默认Object类的equals方法是比较两个对象的地址,跟==的结果一样。
  • 两个对象equals相等,则hashCode一定相等
  • 两个对象equasl不等,但hashCode有可能相等

 

在集合操作的时候有如下规则:

将对象放入到集合中时,首先判断要放入对象的hashcode值与集合中的任意一个元素的hashcode值是否相等,如果不相等直接将该对象放入集合中。如果hashcode值相等,然后再通过equals方法判断要放入对象与集合中的任意一个对象是否相等,如果equals判断不相等,直接将该元素放入到集合中,否则不放入。

 

  • 覆盖equals时总要覆盖hashCode 方法。

HashMap和HashTable

  • HashMap:

public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable

hashmap的键和值都是对象,并且不能包含重复键,但可以包含重复值。HashMap允许null key和null value,但是hashtable不允许。

HashMap是Hashtable的轻量级实现,但是HashMap不是线程安全的,由于非线程安全,效率上可能高于Hashtable。

 

hashMap工作原理:

  • 通过put()方法保存键和值时,先对key调用hashCode()方法,通过返回的hashCode用来找到bucket位置来存储Entry对象(Entry对象类似于链表的节点),而在bucket中存储的是keyvalue,作为map.Entry。
  • 当两个对象的hashCode相同时,bucket位置相同,这时就会发生碰撞,因为HashMap使用链表存储对象,所以会去找是否存在该key,如果存在则替换其value,不存在则将这个Entry存储在链表中。
  • 当用get查找时,会根据key计算hashCode找到对应bucket,然后根据key查找对应的Entry( 找到bucket位置之后,会调用keys.equals()方法去找到链表中正确的节点,最终找到要找的值对象),找到后将value返回。
  • 如果HashMap的大小超过了负载因子(load factor)定义的容量(默认的负载因子大小为0.75),也就是说,当一个map填满了75%的bucket时候,和其它集合类(如ArrayList等)一样,将会创建原来HashMap大小的两倍的bucket数组,来重新调整map的大小,并将原来的对象放入新的bucket数组中。这个过程叫作rehashing。
  • 当重新调整HashMap大小时,在多线程的情况下,可能产生条件竞争(race condition)。因为如果两个线程都发现HashMap需要重新调整大小了,它们会同时试着调整大小。在调整大小的过程中,存储在链表中的元素的次序会反过来,因为移动到新的bucket位置的时候,HashMap并不会将元素放在链表的尾部,而是放在头部,这是为了避免尾部遍历(tail traversing)。如果条件竞争发生了,那么就死循环了。

HashMap源代码:

//初始容量为16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

//最大容量
static final int MAXIMUM_CAPACITY = 1 << 30;

//负载因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;

因为负载因子为0.75,16*0.75=12,所以当保存第12个元素的时候会进行rehashing,会导致输出的结果顺序有变化。

例子:

for(int i = 0;i<20;i++){
    hashMap.put(i+"", i);
}

Iterator iterator = hashMap.entrySet().iterator();
while(iterator.hasNext()){
    System.out.println(iterator.next());
}

输出结果:

11=11
12=12
13=13
14=14
15=15
16=16
17=17
18=18
19=19
0=0
1=1
2=2
3=3
4=4
5=5
6=6
7=7
8=8
9=9
10=10

总结:Set对每个对象只接受一次,并使用自己内部的排序方法(通常,你只关心某个元素是否属于 Set,而不关心它的顺序–否则应该使用List)。Map同样对每个元素保存一份,但这是基于”键”的,Map也有内置的排序,因而不关心元素添加的 顺序。如果添加元素的顺序对你很重要,应该使用 LinkedHashSet或者LinkedHashMap.

HashMap遍历使用Demo:

HashMap<String,Integer> hashMap = new HashMap<>();
long startTime = System.currentTimeMillis();
for(int i = 0;i<TIME;i++){
    hashMap.put(i+"", i);
}
Iterator<Entry<String,Integer>> iterator = hashMap.entrySet().iterator();
while(iterator.hasNext()){
    Entry<String,Integer> entry = iterator.next();
    System.out.println("key is:"+entry.getKey());
    System.out.println("value is:"+entry.getValue());
}

 

  • HashTable:

public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, Serializable

HashTable的许多方法都是用synchronized修饰的。

总结:

HashMap 线程不安全 运行有null的键和值 运行速度快 实现了Map接口
HashTable 线程安全 不允许有null的键和值 占用系统资源多,速度稍慢 Dictionary是过时的了,所以在后面的版本也实现了Map接口

 

 

MessageFormat类

java.text.MessageFormat类可以格式化字符串。

public class MessageFormat extends Format

常用方法:

public static String format(String pattern,Object… arguments)这个方法,后面接受可变参数。

其中可以用占位符表示变化部分,占位符的格式为{ ArgumentIndex , FormatType , FormatStyle }。

使用实例:

System.out.println(MessageFormat.format("My name is:{0},age is:{1,number,integer}",new Object[]{"hy",22}));

输出结果:My name is:hy,age is:22

正向代理和反向代理

正向代理

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 – 仅显示匹配模式的行,而不是整个文件

Vi有三种基本的工作模式:指令行模式、文本输入模式、行末模式。他们的相互关系如所示。
指令模式(Command Mode) 下输入  a、i、o进入文本输入模式(Input Mode)
文本输入模式(Input Mode) 下按ESC进入指令模式(Command Mode)

指令模式(Command Mode)下输入:进入末行模式(Last line Mode)
末行模式(Last line Mode)下指令错误则返回指令模式(Command Mode)
下面分别介绍这三种模式

1、指令模式(Command Mode)
指令模式主要使用方向键移动光标位置进行文字的编辑,下面列出了常用的操作命令及含义。
0  -----光标移动至行首
h  -----光标左移一格
l  -----光标右移一格
j  -----光标下移一行
k  -----光标上移一行
$+A-----将光标移动到该行最后
PageDn ----- 向下移动一页
PageUp ----- 向上移动一页
d+方向键 -----删除文字
dd -----删除整行
pp -----整行复制
r  -----修改光标所在的字符
S  -----删除光标所在的列,并进入输入模式

2、文本输入模式(Input Mode)
在指令模式下(Command Mode)按a/A键、i/I键、o/O键进入文本模式,文本输入模式的命令及其含义如下所示。
a -----在光标后开始插入
A -----在行尾开始插入
i -----从光标所在位置前面开始插入
I -----从光标所在列的第一个非空白字元前面开始插入
o -----在光标所在列下新增一列并进入输入模式
O -----在光标所在列上方新增一列并进入输入模式
ESC -----返回命令行模式

3、末行模式(Last line Mode)
末行模式主要进行一些文字编辑辅助功能,比如字串搜索、替代、保存文件等操作。主要命令如下
:q -----结束Vi程序,如果文件有过修改,先保存文件
:q! -----强制退出Vi程序
:wq  -----保存修改并退出程序
:set nu -----设置行号

     需要注意的是,以上指令都是在英文输入模式下才有效,在linux 终端下有时我们用的是中文输入模式,输入中文的“:”就不能转换模式了。

grep

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

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

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

[root@www ~]# grep [-acinv] [--color=auto] '搜寻字符串' filename 选项与参数:-a :将 binary 文件以 text 文件的方式搜寻数据-c :计算找到 '搜寻字符串' 的次数-i :忽略大小写的不同,所以大小写视为相同-n :顺便输出行号-v :反向选择,亦即显示出没有 '搜寻字符串' 内容的那一行!--color=auto :可以将找到的关键词部分加上颜色的显示喔! tail -fn 2000 server.out |grep -i --color=auto 1010002508 less server.out |grep -E '2014-02-20 13:5[0-4]|2014-02-20 14:[00-10]'|grep 'sql'  --color=auto 

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类型也实现了常量池技术。

实例:

 

public class test {
    public static void main(String[] args) {    
        objPoolTest();
    }

    public static void objPoolTest() {
        int i = 40;
        int i0 = 40;
        Integer i1 = 40;
        Integer i2 = 40;
        Integer i3 = 0;
        Integer i4 = new Integer(40);
        Integer i5 = new Integer(40);
        Integer i6 = new Integer(0);
        Double d1=1.0;
        Double d2=1.0;
        
        System.out.println("i=i0\t" + (i == i0));
        System.out.println("i1=i2\t" + (i1 == i2));
        System.out.println("i1=i2+i3\t" + (i1 == i2 + i3));
        System.out.println("i4=i5\t" + (i4 == i5));
        System.out.println("i4=i5+i6\t" + (i4 == i5 + i6));    
        System.out.println("d1=d2\t" + (d1==d2)); 
        
        System.out.println();        
    }
}

结果:

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