eNSP – 通过华为交换机划分VLAN

实验目标:

    1.通过eNSP实现VLAN的划分
    2.通过实验掌握交换机常用命令使用
    效果:
        将4台同一个子网的机器划分成两个Vlan,只能两两通讯
实验步骤:
 1.初步网络拓扑图
   df39e7bb-c66f-48c5-b586-c5ee073d13f6
2.配置ip和子网掩码
631846ea-bfc5-46d8-bd44-154917aab3dd
3.启动所有设备
    选中设备,右键启动就好
8446fc9b-209c-4bb0-a20e-39ea8af07260
4.测试当前网络状态情况
    当前4台机器是可以互相通讯的,因为处在同一子网、同一交换机中。
4b221994-fd43-433e-9aa0-63a0ac526c55
5.开始配置交换机,网络隔离
    1.进入交换机系统视图
0cc1433d-7473-48b5-9bad-cdac91616162
2.配置交换机GE 0/0/1到GE 0/0/4的插孔link-type为access,默认是hybrid.不能划分vlan
—————————–START——————————–
Please press enter to start cmd line!<Huawei>system-view # 进入系统视图
Enter system view, return user view with Ctrl+Z.
[Huawei]interface GigabitEthernet 0/0/1 # 进入 GE 0/0/1 下面三条命令类似
[Huawei-GigabitEthernet0/0/1]port link-type access # 修改 link-type 为 access,否则在给vlan添加时会报错
[Huawei-GigabitEthernet0/0/1]q # 返回到系统视图[Huawei]interface GigabitEthernet 0/0/2
[Huawei-GigabitEthernet0/0/2]port link-type access
[Huawei-GigabitEthernet0/0/2]q

[Huawei]interface GigabitEthernet 0/0/3
[Huawei-GigabitEthernet0/0/3]port link-type access
[Huawei-GigabitEthernet0/0/3]q
[Huawei]q

[Huawei]interface GigabitEthernet 0/0/4
[Huawei-GigabitEthernet0/0/4]port link-type access
[Huawei-GigabitEthernet0/0/4]q

[Huawei]vlan 520 # 创建vlan 520
[Huawei-vlan520]port GigabitEthernet 0/0/1 # 将GE 0/0/1 和 2 添加进去
[Huawei-vlan520]port GigabitEthernet 0/0/2
[Huawei-vlan520]q

[Huawei]display current-configuration # 查看当前配置
#
sysname Huawei
#
vlan batch 520
#
cluster enable
ntdp enable
ndp enable
#
drop illegal-mac alarm
#
diffserv domain default
#
drop-profile default
#
aaa
authentication-scheme default
authorization-scheme default
accounting-scheme default
domain default
domain default_admin
local-user admin password simple admin
local-user admin service-type http
#
interface Vlanif1
#
interface MEth0/0/1
#
interface GigabitEthernet0/0/1 # 这里能看到4个设备的type已经是access了,并且前面二者也属于vlan 520了
port link-type access
port default vlan 520
#
interface GigabitEthernet0/0/2
port link-type access
port default vlan 520
#
interface GigabitEthernet0/0/3
port link-type access
#
interface GigabitEthernet0/0/4
port link-type access
#

# 省略部分vlan信息

VID Type Ports
——————————————————————————–
1 common UT:GE0/0/3(U) GE0/0/4(U) GE0/0/5(D) GE0/0/6(D)
GE0/0/7(D) GE0/0/8(D) GE0/0/9(D) GE0/0/10(D)
GE0/0/11(D) GE0/0/12(D) GE0/0/13(D) GE0/0/14(D)
GE0/0/15(D) GE0/0/16(D) GE0/0/17(D) GE0/0/18(D)
GE0/0/19(D) GE0/0/20(D) GE0/0/21(D) GE0/0/22(D)
GE0/0/23(D) GE0/0/24(D)
520 common UT:GE0/0/1(U) GE0/0/2(U) # 此时只有 vlan 520 关联了GE 0/0/1 和 2 。下面再创建vlan 530 关联 3 和 4

VID Status Property MAC-LRN Statistics Description
——————————————————————————–
1 enable default enable disable VLAN 0001
520 enable default enable disable VLAN 0520
[Huawei]vlan 530
[Huawei-vlan530]port GigabitEthernet 0/0/3
[Huawei-vlan530]port GigabitEthernet 0/0/4
Jul 31 2015 20:28:54-08:00 Huawei DS/4/DATASYNC_CFGCHANGE:OID 1.3.6.1.4.1.2011.5.
25.191.3.1 configurations have been changed. The current change number is 18, the
change loop count is 0, and the maximum number of records is 4095.
Jul 31 2015 20:28:54-08:00 Huawei %%01IFNET/4/IF_STATE(l)[0]:Interface Vlanif1 ha
s turned into DOWN state.
[Huawei-vlan530]q
[Huawei]display vlan
——————————————————————————–
U: Up; D: Down; TG: Tagged; UT: Untagged;
MP: Vlan-mapping; ST: Vlan-stacking;
#: ProtocolTransparent-vlan; *: Management-vlan;
——————————————————————————–

VID Type Ports
——————————————————————————–
1 common UT:GE0/0/5(D) GE0/0/6(D) GE0/0/7(D) GE0/0/8(D)
GE0/0/9(D) GE0/0/10(D) GE0/0/11(D) GE0/0/12(D)
GE0/0/13(D) GE0/0/14(D) GE0/0/15(D) GE0/0/16(D)
GE0/0/17(D) GE0/0/18(D) GE0/0/19(D) GE0/0/20(D)
GE0/0/21(D) GE0/0/22(D) GE0/0/23(D) GE0/0/24(D)
520 common UT:GE0/0/1(U) GE0/0/2(U)
530 common UT:GE0/0/3(U) GE0/0/4(U)

VID Status Property MAC-LRN Statistics Description
——————————————————————————–
1 enable default enable disable VLAN 0001
520 enable default enable disable VLAN 0520
530 enable default enable disable VLAN 0530
[Huawei]

—————————–END——————————–
3.测试网络隔离效果
        PC1的测试:
         8932c39c-0da1-46cc-8f67-bb91f1358277
      PC3的测试:
           240f1575-4932-46fc-8204-5575ed91b75d
到这里,通过划分VLAN来隔离网络的效果就实现了。
转载请注明出处,本文地址:eNSP – 通过华为交换机划分VLAN

Linux – Virsh

acd4643d-baeb-4135-924f-3c31ad1ba6ac

virsh命令
suspend
resume
dumpxml
 
KVM平台以存储池的形式对存储进行统一管理,所谓存储池可以理解为本地目录、通过远端磁盘阵列(iSCSINFS)分配过来磁盘或目录,当然也支持各类分布式文件系统。
存储池(Storage pools)是放置虚拟机的存储位置,可以是本地的,也可以是网络存储,具体的虚拟机实例放置在卷(Volume)上。
KVM创建的存储池可以理解为一种映射关系,即将某一块挂接至宿主机器上的存储空间形成可被KVM使用的逻辑存储池,以方便虚拟主机的管理。
 
定义一个存储池
pool-define-as images-pool dir - - - - /cloud/images/pool
查看已定义的存储池
pool-list --all
建立基于文件夹的存储池
pool-build images-pool
启用存储池
pool-start images-pool
设置存储池自动运行
pool-autostart images-pool
查看存储池信息
pool-info images-pool
xml格式显示存储池信息
pool-dumpxml images-pool
 
通过XML创建一个存储池
先创建一个test-pool.xml
<pool type='dir'>
<name>test-pool</name>
<source>
</source>
<target>
<path>/cloud/imgs</path>
<permissions>
<mode>0755</mode>
<owner>0</owner>
<group>0</group>
</permissions>
</target>
</pool>
 
pool-define test-pool.xml
pool-build test-pool
pool-start test-pool
pool-dumpxml test-pool
 
创建卷
vol-create-as images-pool billstudy-centos.img 8G --format qcow2
查看存储池里面的卷信息
vol-list images-pool
查看某个卷的信息
vol-info billstudy-centos.img images-pool
更详细卷的信息
vol-dumpxml billstudy-centos.img images-pool
 
 
关于KVM的快照,如果没有快照的话,虚拟机也就失去了很大的作用。
创建快照
snapshot-create-as --domain vm03 --name 2015-02-19 --description "mv03 snap"
查看快照
snapshot-list vm03
查看快照配置
snapshot-current vm03
回复快照位置
snapshot-revert vm03 2015-02-19
删除快照
snapshot-delete vm03 2015-02-19
获取帮助
 
#virt-install快速安装或启动虚拟机
virt-install \
--connect qemu:///system \
--virt-type kvm \
--name centos \
--ram 2048 \
--disk path=/var/lib/libvirt/images/centos.img,format=qcow2,size=8 \
--network bridge=br0 \
--graphics vnc,port=5901,listen=0.0.0.0 \
--cdrom /cloud/CentOS-6.6-x86_64-minimal.iso \
--autostart
 
#克隆虚拟机镜像
virt-clone -o centos-new -n centos6.6 -f /cloud/centos.template
 
#动态插入磁盘
attach-disk centos-new /cloud/abc.raw vdc
#卸载磁盘
detach-disk centos-new vdb


							

NFS安装和配置

——————————————————————————————————————
NFS就是Network File System的缩写,最早是由Sun公司发展开发出来的,它最大的功能就是可以通过网络,让不同计算机,不同操作系统,可以共享资源文件。用户和程序可以像访问本地文件一样访问NFS上的文件
因为NFS支持的功能相当的多,而不同的功能都会使用不同的程序来启动,每启动一个功能就会启用一些端口来传输数据
NFS需要依赖RPC(Remote Procedure Call),RPC最主要的功能就是在指定每个NFS功能所对应的端口号,并且将其反馈给客户端,让客户端可以连结到正确的端口号上去
所以要启动NFS之前,RPC就要先启动了,否则NFS会无法向RPC注册
rpc.nfsd:
这个daemon主要的功能就是在管理Client是否能够登入主机的权限,其中还包含这个登入者的ID的判断!
rpc.mountd
这个daemon主要的功能,则是在管理NFS的文件系统!当Client端顺利的通过rpc.nfsd而登入主机之后,在他可以使用NFS服务器提供的档案之前,还会经过文件权限(就是那个 -rwxrwxrwx 与 owner, group 那几个权限)的认证程序!他会去读NFS的配置文件/etc/exports来比对Client的权限,当通过这一关之后Client就可以取得使用NFS文件的权限了! (注:这个也是我们用来管理 NFS 分享之目录的权限与安全设定的地方)
rpc.lockd
这个进程可以用在管理文件的锁定 (lock) 用途。为何文件需要『锁定』呢? 因为既然分享的NFS档案可以让客户端使用,那么当多个客户端同时尝试写入某个文件时, 就可能对于该文件造成一些问题啦!这个rpc.lockd则可以用来克服这个问题。但rpc.lockd必须要同时在客户端与服务器端都开启才行喔!此外,rpc.lockd也常与rpc.statd同时启用。
rpc.statd
可以用来检查文件的一致性,与rpc.lockd有关!若发生因为客户端同时使用同一文件造成文件可能有所损毁时,rpc.statd可以用来检测并尝试恢复该文件。与 rpc.lockd 同样的,这个功能必须要在服务器端与客户端都启动才会生效。
rpc.rquotad
处理当用户通过nfsmount到远程服务器时的配额
NFS /etc/exports配置含义
/data1/storage *(rw,async,no_root_squash,no_subtree_check)
/data1/storage 是要共享的目录
*是允许访问的计算机,可以是以下内容
单个机器:一个全限定域名(能够被服务器解析)、主机名(能够被服务器解 析)或IP地址。
通配符指定的主机名,使用 * 或 ?字符来指定一个字符串匹配,IP地址中不使用通配符,*代表允许所有计算机访问共享目录
IP 网络:使用a.b.c.d/z,a.b.c.d是网络,z是子网掩码中的位数(如192.168.0.0/24)。另一种可以接受的格式是 a.b.c.d/netmask,a.b.c.d是网络,netmask是子网掩码(如192.168.70.8/255.255.255.0)。
() 内是共享选项
rw是指客户端拥有读写权限,还有一个ro选项,代表只读
sync 同步写入到内存与硬盘中
async 异步写入,写入时会先放到内存,等硬盘有空档再写入
root_squash 客户机用root用户访问该共享文件夹时,将root用户映射成匿名用户
no_root_squash 客户端使用root访问共享目录时 还是拥有root权限,
root_squash:如果客户端使用 root访问共享目录,会被当作匿名用户(也就是说,权限变的很小,比较安全)
all_squash:无论客户端使用什么用户访问共享目录都会被当作匿名用户
subtree_check 如果共享/usr/bin之类的子目录时,强制NFS检查父目录的权限(默认)
no_subtree_check 和上面相对,不检查父目录权限
其它选项参考man exports
——————————————————————————————————————
#安装NFS(所有节点都要安装)
yum -y install nfs-utils
#创建NFS存储目录
mkdir -p /data/storage
#修改/etc/exports
vi /etc/exports
/data/storage *(rw,async,no_root_squash,no_subtree_check)
#修改/etc/sysconfig/nfs
LOCKD_TCPPORT=32803
LOCKD_UDPPORT=32769
MOUNTD_PORT=892
RQUOTAD_PORT=875
STATD_PORT=662
STATD_OUTGOING_PORT=2020
#修改/etc/sysconfig/iptables
-A INPUT -s 192.168.32.0/24 -m state –state NEW -p udp –dport 111 -j ACCEPT
-A INPUT -s 192.168.32.0/24 -m state –state NEW -p tcp –dport 111 -j ACCEPT
-A INPUT -s 192.168.32.0/24 -m state –state NEW -p tcp –dport 2049 -j ACCEPT
-A INPUT -s 192.168.32.0/24 -m state –state NEW -p tcp –dport 32803 -j ACCEPT
-A INPUT -s 192.168.32.0/24 -m state –state NEW -p udp –dport 32769 -j ACCEPT
-A INPUT -s 192.168.32.0/24 -m state –state NEW -p tcp –dport 892 -j ACCEPT
-A INPUT -s 192.168.32.0/24 -m state –state NEW -p udp –dport 892 -j ACCEPT
-A INPUT -s 192.168.32.0/24 -m state –state NEW -p tcp –dport 875 -j ACCEPT
-A INPUT -s 192.168.32.0/24 -m state –state NEW -p udp –dport 875 -j ACCEPT
-A INPUT -s 192.168.32.0/24 -m state –state NEW -p tcp –dport 662 -j ACCEPT
-A INPUT -s 192.168.32.0/24 -m state –state NEW -p udp –dport 662 -j ACCEPT
#重启防火墙
service iptables restart
#启动NFS
service rpcbind start
service nfs start
chkconfig rpcbind on
chkconfig nfs on
#在KVM的主机上挂载NFS
mount -t nfs 192.168.32.170:/data/storage /mnt/storage
#启动虚拟机时会出现权限的问题
error: Failed to start domain centos-nfs
error: internal error process exited while connecting to monitor: 2015-07-16T21:37:17.473018Z qemu-kvm: -drive file=/mnt/storage/centos.qcow2,if=none,id=drive-virtio-disk0,format=qcow2: could not open disk image /mnt/storage/centos.qcow2: Permission denied
#关闭所有机器的SELinux
sed -i ‘s/SELINUX=enforcing/SELINUX=permissive/’ /etc/selinux/config
sed -i ‘s/SELINUX=enforcing/SELINUX=permissive/’ /etc/sysconfig/selinux
setenforce 0
#然后重新启动libvirt
service libvirtd restart

KVM 【SNAT/DNAT2种配置实现以及扁平化网络模式(flat)实现/virsh2种动态迁移实现】

-------------------------------------------------------------------------
###KVM SNAT配置过程
-------------------------------------------------------------------------
#增加一个br0网桥(不跟任何网卡绑定)
brctl addbr br0
#激活网桥
ifconfig br0 up
#给br0配置IP地址
ifconfig br0 10.0.0.1/24
 
#1.使用virsh创建两台虚拟机vm01和vm02,这两台虚拟机网卡都接到br0上
#2.ip地址分别为10.0.0.11、10.0.0.12(注意:两台虚拟机的mac不能相同)
#3.将这两台虚拟机的网关设置为10.0.0.1即br0的ip地址
 
#测试用vm1 ping 10.0.0.1可以ping通,用vm2 ping 10.0.0.1也可以ping通
#但是vm1 ping 不通vm2
#需要在宿主机中开启ipv4转发功能。编辑/etc/sysctl.conf修改内容为net.ipv4.ip_forward=1,然后执行sysctl -p使修改立即生效。
#开启后vm1和vm2即可ping通
 
#在nat表上添加SNAT转发功能即来自10.0.0.0/24的数据包且从eth0出去,将其源地址转换成192.168.50.150
iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -o eth0 -j SNAT --to-source 192.168.50.150
#要实现SNAT功能,必须让filter表的FORWARD链允许转发
iptables -t filter -I FORWARD -p all -s 10.0.0.0/24 -j ACCEPT
iptables -t filter -I FORWARD -p all -d 10.0.0.0/24 -j ACCEPT
或者将filterFORWARD最后一条拒绝的链删除
iptables -D FORWARD 1
-------------------------------------------------------------------------
 
 
-------------------------------------------------------------------------
###KVM DNAT配置过程,将宿主机的8022端口映射到客户机vm1的22端口
-------------------------------------------------------------------------
#重新启动iptables,恢复iptables配置
service iptables restart
#设置宿主机iptables允许tcp8022端口通过
iptables -I INPUT -p tcp --dport 8022 -j ACCEPT
#设置nat表的PREROUTING链,将访问192.168.50.150 tcp 8022端口的数据转发到10.0.0.11的22号端口
iptables -t nat -A PREROUTING -d 192.168.50.150 -p tcp -m tcp --dport 8022 -j DNAT --to-destination 10.0.0.11:22
#要实现SNAT功能,必须让filter表的FORWARD链允许转发
iptables -t filter -I FORWARD -p all -s 10.0.0.0/24 -j ACCEPT
iptables -t filter -I FORWARD -p all -d 10.0.0.0/24 -j ACCEPT
或者将filterFORWARD最后一条拒绝的链删除
iptables -D FORWARD 1
-------------------------------------------------------------------------
 
 
-------------------------------------------------------------------------
###KVM DNAT配置过程,将宿主机的一个单独ip(如192.168.50.150)映射到客户机
#ip为10.0.0.11的vm1虚拟机上,即IP一对一映射
-------------------------------------------------------------------------
#重新启动iptables,恢复iptables配置
service iptables restart
#设置nat表的PREROUTING链,将访问192.168.50.150的转发到10.0.0.11
iptables -t nat -A PREROUTING -d 192.168.50.150 -j DNAT --to-destination 10.0.0.11
#设置nat表的POSTROUTING链,将来自10.0.0.11的数据包的源地址转换成192.168.50.150
iptables -t nat -A POSTROUTING -o eth0 -s 10.0.0.11 -j SNAT --to-source 192.168.50.150
#要实现SNAT功能,必须让filter表的FORWARD链允许转发
iptables -t filter -I FORWARD -p all -s 10.0.0.0/24 -j ACCEPT
iptables -t filter -I FORWARD -p all -d 10.0.0.0/24 -j ACCEPT
或者将filterFORWARD最后一条拒绝的链删除
iptables -D FORWARD 1
-------------------------------------------------------------------------
 
 
-------------------------------------------------------------------------
###KVM 扁平的网络模式(flat)配置过程,即将虚拟机分配一个内网ip一个外网ip
-------------------------------------------------------------------------
#在宿主机上配置两个网卡eth0和eth1,eth0作为外网eth1作为内网
#如果是使用的是vmware workstation虚拟出的宿主机,eth0选用NAT网络eth1选用host-only网络
#将eth0不设置IP,eth1设置ip为192.168.80.150
#创建两个网桥br0和br1,将br1与eth0连接到一起
#使用virsh的iface-bridge命令非常方便,创建br1的同时再与eth0连接
iface-bridge eth0 br1
#注意:上面的命令有可能失败,因为如果安装了图形桌面的CentOS,NetworkManager接管网络,
#需要将NetworkManager停掉,并重新启动network服务来接管网络服务
chkconfig NetworkManager off
service NetworkManager stop
service network restart
#使用virsh创建虚拟机,在其配置文件中有两个网卡,注意mac地址要唯一
<interface type="bridge">
<mac address='06:64:12:00:00:54'/>
<!--当前主机网桥的名称-->
<source bridge="br0" />
<model type='virtio'/>
</interface>
<interface type="bridge">
<mac address='06:64:12:00:00:55'/>
<!--当前主机网桥的名称-->
<source bridge="br1" />
<model type='virtio'/>
</interface>
#在客户机中设置eth0的ip为192.168.50.200/24,网关为192.168.50.2
#在客户机中设置eth1的ip为10.0.0.11/24,不设置网关
-------------------------------------------------------------------------
 
virsh
两种模式:交换模式、非交互模式
suspend
resume
dumpxml
 
KVM平台以存储池的形式对存储进行统一管理,所谓存储池可以理解为本地目录、通过远端磁盘阵列(iSCSINFS)分配过来磁盘或目录,当然也支持各类分布式文件系统。
存储池(Storage pools)是放置虚拟机的存储位置,可以是本地的,也可以是网络存储,具体的虚拟机实例放置在卷(Volume)上。
KVM创建的存储池可以理解为一种映射关系,即将某一块挂接至宿主机器上的存储空间形成可被KVM使用的逻辑存储池,以方便虚拟主机的管理。
 
定义一个存储池
pool-define-as images-pool dir - - - - /cloud/images/pool
查看已定义的存储池
pool-list --all
建立基于文件夹的存储池
pool-build images-pool
启用存储池
pool-start images-pool
设置存储池自动运行
pool-autostart images-pool
查看存储池信息
pool-info images-pool
xml格式显示存储池信息
pool-dumpxml images-pool
 
通过XML创建一个存储池
先创建一个test-pool.xml
<pool type='dir'>
<name>test-pool</name>
<source>
</source>
<target>
<path>/cloud/imgs</path>
<permissions>
<mode>0755</mode>
<owner>0</owner>
<group>0</group>
</permissions>
</target>
</pool>
 
pool-define test-pool.xml
pool-build test-pool
pool-start test-pool
pool-dumpxml test-pool
 
创建卷
vol-create-as images-pool billstudy-centos.img 8G --format qcow2
查看存储池里面的卷信息
vol-list images-pool
查看某个卷的信息
vol-info billstudy-centos.img images-pool
更详细卷的信息
vol-dumpxml billstudy-centos.img images-pool
 
 
关于KVM的快照,如果没有快照的话,虚拟机也就失去了很大的作用。
创建快照
snapshot-create-as --domain vm03 --name 2015-02-19 --description "mv03 snap"
查看快照
snapshot-list vm03
查看快照配置
snapshot-current vm03
回复快照位置
snapshot-revert vm03 2015-02-19
删除快照
snapshot-delete vm03 2015-02-19
获取帮助
help snapshot
 
 
QEMU monitorQEMU与用户交互的一种控制台,一般用于为QEMU模拟器提供较为复杂的功能,
 
QEMU monitor的切换
"Ctrl + Alt + 2" --> QEMU monitor状态
"Ctrl + Alt + 1" --> 客户机标准显示窗口
 
 
查看帮助信息
help/? [cmd]
help savevm ? savevm
 
 
显示系统命令状态:info
 
查看QEMU的版本信息
info versions
查看当前QEMU是否有KVM的支持
info kvm
info name
info status
查看客户机各个vCPU的信息
info cpus
查看块设备信息,如硬盘、软盘、光驱等
info block
查看客户机的网络配置信息
info network
显示当前系统中已保存的客户机快照信息
info snapshots
查看当前客户机的迁移状态
info migrate
显示当前客户机的VNC状态
info vnc
查看QEMU monitor中命令执行的历史
info history
 
 
创建快照
savevm snap20150406
恢复快照
loadvm snap20150406
删除快照
delvm snap20150406
 
 
 
-------------------------------------------------------------------------
###虚拟机动态迁移
-------------------------------------------------------------------------
安装NFS并使用NFS网络存储
 
动态迁移:将客户机从源宿主机 -> 目的宿主机
注意:都要挂载NFS
1.在源宿主机上挂载NFS
2.在源宿主机上启动虚拟机
3.在目的宿主机上挂载NFS
4.在目的宿主机上启动虚拟机
5.关闭目的宿主机的防火墙或开启相应端口
/usr/libexec/qemu-kvm -m 4096 -smp 1 -incoming tcp:0:8888 /mnt/storage/centos.qcow2 -vnc :1 &
-incoming 这个参数使qemu-kvm进程进入到迁移监听模式
tcp:0:8888 这个参数表示在8888端口上建立一个TCP Socket连接用于接收来自源宿主机动态迁移的内容,"0"表示允许来自任何主机的连接
5.在源宿主机上切换到QEMU monitor,然后执行迁移命令
migrate tcp:192.168.50.160:8888
6.查看迁移结果
 
使用virsh进行热迁移
migrate centos-nfs --live qemu+ssh://192.168.50.160/system --unsafe
#出现错误1
error: internal error Attempt to migrate guest to the same host 564d46b4-6d6b-2a13-3f81-bb970716d517
sed -i "/#host_uuid/ahost_uuid = \"`uuidgen`\"" /etc/libvirt/libvirtd.conf
#出现错误2
error: Unable to resolve address 'kvm02.billstudy.com' service '49152': No address associated with hostname
vi /etc/hosts
192.168.50.150 kvm01.billstudy.com
192.168.50.160 kvm02.billstudy.com

Vmware qemu-kvm 虚拟化测试

[root@kvm1 cloud]# lsmod | grep kvm
kvm_intel 55496 3
kvm 337772 1 kvm_intel
[root@kvm1 cloud]# egrep "(vmx|svm)" --color /proc/cpuinfo
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts xtopology tsc_reliable nonstop_tsc aperfmperf unfair_spinlock pni pclmulqdq vmx ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm ida arat epb xsaveopt pln pts dts tpr_shadow vnmi ept vpid fsgsbase smep
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts xtopology tsc_reliable nonstop_tsc aperfmperf unfair_spinlock pni pclmulqdq vmx ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm ida arat epb xsaveopt pln pts dts tpr_shadow vnmi ept vpid fsgsbase smep
[root@kvm1 cloud]#

 

如果没有结果,则应该开启机器支持Inter VT-x/EPT or AMD-V/RVI

7888de1f-387f-4dfd-886d-2475b1d02b92

 

 


#查看Linux内核是否加载了kvm模块
lsmod | grep kvm
#安装qemu-kvm,安装完成后会生成/usr/libexec/qemu-kvm,是用户空间操作kvm内核的接口
yum install qemu-kvm
#通过网络上传ISO镜像文件
#生成一个8G的文件(文件内容全部是0),作为虚拟机的磁盘
dd if=/dev/zero of=/cloud/CentOS.img bs=1M count=8192
#使用qemu-kvm创建虚拟机(参数说明 -m 指定内存大小 -smp 指定cup数量 -boot启动顺序)
/usr/libexec/qemu-kvm -m 2048 -smp 1 -boot order=cd -hda /cloud/CentOS.img -cdrom /iso/CentOS-6.6-x86_64-minimal.iso -vnc :1
参数说明:
-m 指定客户机内存大小,默认单位MB
-smp 指定客户机cpu个数。SMP:(symmetric[sɪ'metrɪk'] Multi-processing)对称多处理
-boot 指定客户机系统启动顺序,order=cd是指先光盘再硬盘。c:CD-ROM d:Hard Disk
-hda 指定客户机的IDE硬盘(即前面的创建的镜像文件)
-cdrom 指定客户机的光驱
-vnc 类似window下的远程桌面
:1 端口为 5900 + 1 = 5901
#安装vnc或使用vnc工具连接虚拟机,给虚拟机安装系统
#关闭虚拟机并再次启动虚拟机,指定vnc端口为5901(不需要指定cdrom,因为已经将操作系统安装到磁盘中了)
/usr/libexec/qemu-kvm -m 2048 -smp 1 -boot order=cd -hda /cloud/CentOS.img -vnc :1
#查看虚拟机信息
free -m
dmesg | grep Memory
查看cpu信息命令:
lscpu
cat /proc/cpuinfo
逻辑CPU个数
cat /proc/cpuinfo | grep "processor" | wc -l
物理CPU个数
cat /proc/cpuinfo | grep "physical id" | sort | uniq | wc -l
每个物理CPU上core的个数(未计入超线程)
cat /proc/cpuinfo | grep "core id" | sort | uniq | wc -l
#修改配置内存和CPU参数
/usr/libexec/qemu-kvm -m 4096 -smp 2,cores=4 -boot order=cd -hda /cloud/CentOS.img -vnc :1
n[,maxcpus=cpus][,cores=cores][,threads=threads][,sockets=sockets]
set the number of CPUs to 'n' [default=1]
maxcpus= maximum number of total cpus, including offline CPUs for hotplug etc.(客户机最大可能被使用的CPU输了,包括启动时处于offline状态的cpu数量,可用于热插拔加入CPU,但是不能超过maxcpus这个上限)
cores=number of CPU cores on one socket.(就是我们平时说的核,每个物理CPU可以双核,四核等等)
threads=number of threads on one CPU core.(thread就是每个core的硬件线程数,即超线程)
sockets=number of discrete sockets in the system.(就是主板上插cpu的槽的数目,也就是可以插入的物理CPU的个数)
#qemu-img是一个比较重要的工具,是QEMU的磁盘管理工具。
#查看帮助信息
qemu-img -h
#查看镜像文件信息
qemu-img info /cloud/CentOS.img
#创建一个磁盘镜像文件
qemu-img create -f qcow2 /cloud/bill.img 10G
#将raw格式转换成qcow2格式
qemu-img convert -O qcow2 /cloud/bill.raw /cloud/bill.qcow2
#修改镜像大小
qemu-img resize /cloud/test.img +2G
#检查镜像
qemu-img check /cloud/bill.qcow2
#镜像格式
#qemu-img支持多种镜像格式,可通过qemu-img -h命令查看,常用的有qcow2,raw
#raw:原始的磁盘镜像格式,是qemu-img命令默认的文件格式,这种格式的文件的优势在于它非常简单且非常容易移植到其他模拟器上使用。特点一开始就让镜像实际占用了分配空间,但是写入速度稍快一下。
#qcow2:这种格式是QEMU目前推荐的镜像格式,它是功能最多的格式,它支持稀疏文件以节省存储空间,支持加密、压缩、快照等功能
QEMU monitor是QEMU与用户交互的一种控制台,一般用于为QEMU模拟器提供较为复杂的功能,
QEMU monitor的切换
"Ctrl + Alt + 2" --> QEMU monitor状态
"Ctrl + Alt + 1" --> 客户机标准显示窗口
查看帮助信息
help/? [cmd]
help savevm 或 ? savevm
显示系统命令状态:info
查看QEMU的版本信息
info versions
查看当前QEMU是否有KVM的支持
info kvm
info name
info status
查看客户机各个vCPU的信息
info cpus
查看块设备信息,如硬盘、软盘、光驱等
info block
查看客户机的网络配置信息
info network
显示当前系统中已保存的客户机快照信息
info snapshots
查看当前客户机的迁移状态
info migrate
显示当前客户机的VNC状态
info vnc
查看QEMU monitor中命令执行的历史
info history
创建快照
savevm snap20150406
恢复快照
loadvm snap20150406
删除快照
delvm snap20150406

Linux bridge-utils tunctl 使用

网络
brctlLinux下用来管理以太网桥,在内核中建立、维护、检查网桥配置的命令
STP Spanning Tree Protocol(生成树协议)逻辑上断开环路,防止二层网络的广播风暴的产生
dhcp模式启用 'eth0'
 
在计算机网络中,TUNTAP是操作系统内核中的虚拟网络设备。不同于普通靠硬件网路板卡实现的设备,这些虚拟的网络设备全部用软件实现,并向运行于操作系统上的软件提供与硬件的网络设备完全相同的功能。
TAP等同于一个以太网设备,它操作第二层数据包如以太网数据帧。TUN模拟了网络层设备,操作第三层数据包比如IP数据封包。
 
#1.创建kvm桥接网络模式,要安装bridge-utils tunctl
yum install bridge-utils tunctl
 
添加一个br0网桥(桥接类型)
brctl addbr br0
ifconfig br0 up
 
#分步执行网络会断开
------------------------
br0eth0绑定在一起
brctl addif br0 eth0
br0设置为启用STP协议
brctl stp br0 on
eth0IP设置为0
ifconfig eth0 0
使用dhcpbr0分配IP
dhclient br0
-------------------------
 
#最佳方式(注意修改成自己的IP)
brctl addif br0 eth0 && brctl stp br0 on && ifconfig eth0 0.0.0.0 && ifconfig br0 192.168.52.201 netmask 255.255.255.0 && route add default gw 192.168.52.1
 
#创建TAP类型虚拟网卡设备
tunctl -b -t vnet0
ifconfig vnet0 up
brctl addif br0 vnet0
brctl show
 
#创建虚拟机并关联网卡
/usr/libexec/qemu-kvm -m 4096 -smp 1 -boot order=cd -hda /cloud/Centos.img -net nic -net tap,ifname=vnet0,script=no,downscript=no
#创建虚拟机并关联网卡并添加mac地址
/usr/libexec/qemu-kvm -m 2048 -smp 1 -boot order=cd -hda /cloud/Centos.img -net nic,macaddr=52:54:00:12:34:57 -net tap,ifname=vnet0,script=no,downscript=no
 
#将磁盘设置成半虚拟化virtio
<disk type="file" device="disk">
<driver name="qemu" type="qcow2" />
<source file="/cloud/centos.img" />
<target dev='vda' bus='virtio'/>
</disk>
 
 
 
libvirt
libvirt是一套免费、开源的支持Linux下主流虚拟化工具的C函数库,其旨在为包括Xen在内的各种虚拟化工具提供一套方便、可靠的编程接口,支持与C,C++,Ruby,Python,JAVA等多种主流开发语言的绑定。当前主流Linux平台上默认的虚拟化管理工具virt-manager(图形化),virt-install(命令行模式)等均基于libvirt开发而成。
Libvirt库是一种实现 Linux 虚拟化功能的 Linux API,它支持各种虚拟机监控程序,包括 Xen KVM,以及 QEMU 和用于其他操作系统的一些虚拟产品
 
#安装libvirt
yum install libvirt
#启动libvirt
service libvirtd start
#启动后会多一个virbr0网桥,该网桥是NAT类型
 
virsh(非常好的虚拟化命令行管理工具,两种模式:交换模式和非交换模式)
定义虚拟机
virsh define /cloud/centos-base.xml
 
virsh 进行管理虚拟机
virsh# list --all # 显示所有虚拟机 --all显示全部
 
启动虚拟机
#virsh start centos
 
关闭虚拟机
#virsh shutdown centos
 
强制关机
#virsh destroy centos
 
移除虚拟机
#virsh undefine centos
 
显示vnc端口
#virsh vncdisplay centos
 
动态查询kvm使用资源
#top -d 1 | grep kvm
 
查询kvm进程
ps -aux | grep kvm
 
开机自动启动虚拟机
#virsh autostart centos
 
导出虚拟机centos的硬件配置信息为/cloud/centos.bak.xml
#virsh dumpxml centos > /cloud/centos.bak.xml
 
编辑虚拟机配置
#virsh edit centos

 

03-类加载器

类加载器

    
 
    类加载定义:通过一个类的全限定名来获取描述此类的二进制流来获取所需要的类的动作
        
    类从被加载到虚拟机内存中开始,到卸载出内存生命周期分为以下7个阶段
        加载(Loading) -> 【验证(Verification) -> 准备(Preparation) -> 解析(Resolution)】 -> 初始化(Initialization) -> 使用(Using) -> 卸载(Unloading)
        验证、准备和解析统称为连接(Linking)
    
    主动引用(会触发类初始化的引用):
        1.遇到new,getstatic,putstatic或invokestatic这4条指令时,若类没有初始化会被触发初始化(new对象时、读取或者设置类静态非常量字段时、调用类静态方法)
        2.反射调用类时
        3.初始化类,其父类还未被初始化时触发父类初始化
        4.虚拟机启动时,用户需要指定一个要执行的主类(包含main方法的那个类),虚拟机会先初始化这个类
    被动引用(不会触发类初始化的引用):
        1.通过子类引用父类的静态字段
        2.通过定义数组来引用类
        3.在A类中用到了B类的静态常量,这里B类不会被初始化。常量在编译阶段会存到调用类的常量池中,本质上没有直接调用定义常量的类
    
    双亲委派模型:
        1.虚拟机的角度看类加载器种类:

                a.启动类加载器(Bootstrap ClassLoader),用C++实现。
                b.所有的其他类加载器,都由Java实现。并且都继承于java.lang.ClassLoader类
        2.开发人员的角度看类加载器种类:
                a.启动类加载器(Bootstrap ClassLoader):负责加载<JAVA_HOME>/lib目录下。(按照文件名识别)
                b.扩展类加载器(Extension ClassLoader):这个类加载器由sun.misc.Launcher$ExtClassLoader实现,它负责加载<JAVA_HOME>/lib/ext目录中的,或者被java.ext.dirs系统变量所指定的路径中所有的类库
                c.应用程序类加载器(Application ClassLoader):这个类加载器由sun.misc.Launcher$AppClassLoader实现,由于ClassLoader的getSystemClassLoader方法返回值也是它,所以一般它也被称为系统加载器。
 
        工作过程:
            如果一个类收到了类加载的请求,它首先不会自己去尝试去加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此。
    因此所有的类加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去加载。
    
       双亲委派模型带来的好处:
            1.[稳定性]同一个类始终会给同一个类加载器去加载,例如java.lang.Object在rt.jar中。那么在各种类加载器环境中都会是委派给启动类加载器,所以系统中也只会有一个Objec类
    
 
         ClassLoader实现双亲委派模型源码:
/**
*使用指定的二进制名称来加载类。此方法的默认实现将按以下顺序搜索类: 
1.调用 findLoadedClass(String) 来检查是否已经加载类。
2.在父类加载器上调用 loadClass 方法。如果父类加载器为 null,则使用虚拟机的内置类加载器。 
3.调用 findClass(String) 方法查找类。
如果使用上述步骤找到类,并且 resolve 标志为真,则此方法将在得到的 Class 对象上调用 resolveClass(Class) 方法。 
鼓励用 ClassLoader 的子类重写 findClass(String),而不是使用此方法,ClassLoader的findClass方法默认是抛出ClassNotFoundException

**/ 
protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }
                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);
                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

02-Reference & GC

一、引用
    Java中引用的定义很传统:如果reference类型的数据中存储的数值代表的是另外一块内存的起始地址,就称这块内存代表着一个引用。这个定义很纯粹,但是太过狭隘,一个对象在这种定义下只有被引用或者没有被引用两种状态,对于如何描述一些“食之无味弃之可惜”的对象就显得无能为力。我们希望能描述这样一类对象:当内存空间还足够时,则能保存在内存中;如果内存在进行垃圾收集后还是非常紧张,则可以抛弃这些对象。很多系统的缓存功能都符合这样的应用场景。
    在JDK1.2之后,Java对引用的概念进行了扩充,将引用分为:
        以下四种引用强度依次逐渐减弱
        1.强引用(Strong Reference)
            强引用就是指在程序代码中普遍存在的,类似“Object obj = new Object()”这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。
        2.软引用(Soft Reference)
            软引用用来描述一些还有用,但并非必须的对象。对于软引用关联着的对象,在系统将要发生内存溢出异常之前,会把这些对象列进回收范围之中并进行第二次回收。如果这次回收还是没有足够的内存,才会抛出内存溢出异常。在JDK1.2之后,提供了SoftReference类来实现软引用。
        3.弱引用(Weak Reference)
            弱引用也是用来描述非必须对象的,但是它的强度比弱引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集器发生之前。当垃圾收集器工作时,无论当前的内存是否足够,都会回收掉只被弱引用关联的对象。在JDK1.2之后,提供了WeakReference类来实现弱引用。
        4.虚引用(Phantom Reference)
            虚引用也称为幽灵引用或者幻影引用,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的就是希望能在这个对象被收集器回收时收到一个系统通知。在JDK1.2之后,提供了PhantomReference来实现虚引用。
二、判断对象已死算法
    1.引用计数算法(Reference Counting)
        定义:给对象中添加一个引用计数器,每当有个地方引用它时,计算器值就加一;当引用失效时,计数器值就减1;任何时候计算器都为0的对象就是不可能再被使用的。
        客观地说,引用计数算法的实现简单,判定效率也很高,在大部分情况下它都是一个不错的算法,但是Java中没有选用它来管理内存,其中最主要的原因是它很难解决对象之间的相互循环引用的问题
    2.根搜索算法(GC Roots Tracing)
        基本思路:通过一系列名为”GC Roots”的对象作为起始点,从这些节点开始往下搜索,搜索走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。
        在Java语言里,可作为GC Roots的对象包括以下几种:
        1.虚拟机栈(栈帧中的本地变量表)中引用的对象。
        2.方法区中的类静态属性引用的对象。
        3.方法区中的常量引用的对象。
        4.本地方法栈JNI(即一般说的Native方法)的引用的对象。
三、对象死亡的两次标记过程
    在根搜索算法中不可达的对象,也并非是“非死不可”的,这时候它们暂时处于“缓刑”阶段,要真正宣告一个对象死亡,至少要经过两次标记过程:如果对象在进行根搜索后发现没有与GC Roots相连接的引用链,那它会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize方法或者finalize已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行”。
    经过上面的判定如果有必要执行finalize方法,那么这个对象将会被放置在一个名为F-Queue的队列之中,并在稍后由一条虚拟机自动建立的、低优先级的Finalizer线程去执行。这里所谓的“执行”是指虚拟机会触发这个方法,但并不承诺会等待它运行结束。这样做的原因是,如果一个对象在finalize方法中执行缓慢,或者发生了死循环(更极端的情况),将很可能会导致F-Queue队列中的其他对象永久处于等待状态,甚至导致整个内存回收系统崩溃。finalize方法是对象逃脱死亡命运的最后一次机会,稍后GC将对F-Queue中的对象进行第二次小规模的标记,如果对象要在finalize中拯救自己–只要重新与引用链上的任何一个对象建立关联即可,譬如把自己(this关键字)赋值给某个类的变量或对象的成员变量,那在第二次标记时它将被移除出“即将回收”的集合;如果对象这时候还没有逃脱,那它就真的离死不远了。
代码测试如下:
package com.billstudy.jvm;
/**
 * Created by Bill on 2015-07-16 16:11
 * 测试GC自救,finalize只会被调用一次
 * Email: LuckyBigBill@gmail.com
 */
public class FinalizeEscapeGC {
    public static FinalizeEscapeGC SAVE_HOOK = null;
    public void isAlive(){
        System.out.println("yes, i am still alive :)");
    }
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("finalize method executed!");
        SAVE_HOOK = this;
    }
    public static void main(String[] args) throws Exception{
        SAVE_HOOK = new FinalizeEscapeGC();
        // 对象第一次拯救自己
        SAVE_HOOK = null;
        System.gc();
        // Finalizer方法优先级低,暂停等会儿它
        Thread.sleep(500);
        if (SAVE_HOOK != null){
            SAVE_HOOK.isAlive();
        }else{
            System.out.println("no, i am dead :(");
        }
        // 和上面代码相同,这次自救失败了
        SAVE_HOOK = null;
        System.gc();
        Thread.sleep(500);
        if (SAVE_HOOK != null){
            SAVE_HOOK.isAlive();
        }else{
            System.out.println("no, i am dead :(");
        }
    }
}
// 输出结果
finalize method executed!
yes, i am still alive : )
no, i am dead : (

(更多…)

01-Jvm 内存区域

5849dbe1-8f05-4fd8-b327-5d6f62100d52

  1.程序计数器(Program Counter Register)
        在虚拟机中一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。 在虚拟机的概念模型里(仅是概念模型,各种虚拟机可能会通过一些更高效的方式去实现),字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成
        由于Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有个独立的程序计数器,各条线程之间的计数器互不影响,独立存储。我们称这类内存区域为”线程私有”的内存
    如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行虚拟机字节码指令的地址;如果正在执行的Native方法,这个计算器值则为空(Undefined),此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。
 
    2.Java虚拟机栈(Java Virtual Machine Stacks)    
        与程序计数器一样,Java虚拟机栈也是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作栈、动态链接、方法出口等信息。每个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中充入栈到出栈的过程
        平常大家说堆(Heap)栈(Stack),这种划分比较粗糙。所说的“栈”指的就是虚拟机栈,或者说是虚拟机栈中的局部变量表部分。局部变量表存放了编译期可知的各种基本类型(boolean、byte、char、short、int、float、long、double)、对象引用(Reference类型,它不等同于对象本身,根据不同的虚拟机实现,它可能是一个执行对象起始地址的引用指针,也可能是执行一个代表对象的句柄或其他与此对象相关的位置)和returnAddress类型(指向了一条字节码指令的地址)。
    64位长度的Long和Double类型的数据会占用2个局部变量空间(Slot),其他的数据类型只占用1个。局部变量表所需的内存空间在编译期间完成分配,也就是一个方法的局部变量空间是完成确定的,在方法运行期间不会改变局部变量表的大小。
 
    3.本地方法栈(Native Method Stacks)
        和虚拟机栈发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机只想你个Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Nativce方法服务。虚拟机规范中对本地方法栈的方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。甚至有的虚拟机(譬如Sun HotSpot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。与虚拟机栈一样,本地方法栈也会抛出StackOverflowError和OutOfMemoryError异常。
    4.Java堆
        对大多数应用来说,Java堆(Java Heap)是Java虚拟机所管理的内存最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。词内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。这一点在Java虚拟机规划中的描述是:所有的对象实例以及数组都要在堆上分配,但是随着JIT编译器的发展与逃逸分析技术的逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化发生,所有的对象都分配在对上也渐渐变得不是那么“绝对了”。
    同时Java堆还是垃圾收集器管理的主要区域,因此很多时候也被称作”GC 堆”(Garbage Collected Heap)。如果从内存回收的角度看,由于现在的收集器基本都是采用的分代收集算法,所以Java堆中还可以细分为:新生代和老年代
 
    5.方法区
        方法区(Method Area)和Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器后的代码等数据。虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做Non-Heap(非堆),目的应该是与Java堆区分开来。
    6.运行时常量池(Runtime Constant Pool)
        运行时常量池是方法区是一部分,Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译器生成的各种字面量和符号引用,这部分内存将在类加载后存放到方法区的运行时常量池中。
        相对于Class文件常量池的另外一个重要特征是具备动态性,Java语言并不要求常量一定只能在编译期产生,也就是并非预置入Class文件中常量池的内存才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中,这种特性被开发人员利用得比较多的便是String类的intern方法。
        既然运行时常量池是方法区的一部分,自然会收到方法区内存的限制,当常量池无法再申请到内存时会抛出OutOfMemryError异常。
    
    7.直接内存(Direct Memory)
        直接内存并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用,而且也可能导致OutOfMemoryError异常出现。
        在JDK1.4中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。
        显然,本机直接的内存分配不会收到Java堆大小的限制,但是,既然是内存,则肯定还是会受到本机总内存(包括RAM以及SWAP区或者分页文件)的大小及处理器寻址空间的限制。服务器管理员配置虚拟机参数时,一般会根据实际内存设置-Xmx等参数信息,但经常会忽略掉直接内存,使得各个内存区域的总和大于物理内存限制(包物理上的和操作系统级的限制),从而导致动态扩展时出现OutOfMemoryError异常。

 

03-类加载器

类加载器

    

    类加载定义:通过一个类的全限定名来获取描述此类的二进制流来获取所需要的类的动作
        
    类从被加载到虚拟机内存中开始,到卸载出内存生命周期分为以下7个阶段
        加载(Loading) -> 【验证(Verification) -> 准备(Preparation) -> 解析(Resolution)】 -> 初始化(Initialization) -> 使用(Using) -> 卸载(Unloading)
        验证、准备和解析统称为连接(Linking)
    
    主动引用(会触发类初始化的引用):
        1.遇到new,getstatic,putstatic或invokestatic这4条指令时,若类没有初始化会被触发初始化(new对象时、读取或者设置类静态非常量字段时、调用类静态方法)
        2.反射调用类时
        3.初始化类,其父类还未被初始化时触发父类初始化
        4.虚拟机启动时,用户需要指定一个要执行的主类(包含main方法的那个类),虚拟机会先初始化这个类

    被动引用(不会触发类初始化的引用):
        1.通过子类引用父类的静态字段
        2.通过定义数组来引用类
        3.在A类中用到了B类的静态常量,这里B类不会被初始化。常量在编译阶段会存到调用类的常量池中,本质上没有直接调用定义常量的类
        
    双亲委派模型:
        1.虚拟机的角度看类加载器种类:

             
  a.启动类加载器(Bootstrap ClassLoader),用C++实现。
             
  b.所有的其他类加载器,都由Java实现。并且都继承于java.lang.ClassLoader类

        2.开发人员的角度看类加载器种类:
             
  a.启动类加载器(Bootstrap ClassLoader):负责加载/lib目录下。(按照文件名识别)

             
  b.扩展类加载器(Extension ClassLoader):这个类加载器由sun.misc.Launcher$ExtClassLoader实现,它负责加载/lib/ext目录中的,或者被java.ext.dirs系统变量所指定的路径中所有的类库

             
  c.应用程序类加载器(Application ClassLoader):这个类加载器由sun.misc.Launcher$AppClassLoader实现,由于ClassLoader的getSystemClassLoader方法返回值也是它,所以一般它也被称为系统加载器。


        工作过程:
            如果一个类收到了类加载的请求,它首先不会自己去尝试去加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此。
    因此所有的类加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去加载。
    
       双亲委派模型带来的好处:
            1.[稳定性]同一个类始终会给同一个类加载器去加载,例如java.lang.Object在rt.jar中。那么在各种类加载器环境中都会是委派给启动类加载器,所以系统中也只会有一个Objec类
    

         ClassLoader实现双亲委派模型源码:

/**
*使用指定的二进制名称来加载类。此方法的默认实现将按以下顺序搜索类: 
1.调用 findLoadedClass(String) 来检查是否已经加载类。
2.在父类加载器上调用 loadClass 方法。如果父类加载器为 null,则使用虚拟机的内置类加载器。 
3.调用 findClass(String) 方法查找类。
如果使用上述步骤找到类,并且 resolve 标志为真,则此方法将在得到的 Class 对象上调用 resolveClass(Class) 方法。 
鼓励用 ClassLoader 的子类重写 findClass(String),而不是使用此方法,ClassLoader的findClass方法默认是抛出ClassNotFoundException

**/ 
protected Class loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }
                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);
                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

版权声明:本文为博主原创文章,未经博主允许不得转载。