jvm基础学习

1、jvm虚拟机内存模型

程序计数器:是线程的私有空间,每个线程都有一个独立的程序计数器,用于记录下一指令。

java虚拟机栈:和线程创建的时候一起创建的,也是线程的私有空间,它保存了线程的局部变量、执行结果、调用和返回的情况。
在虚拟机栈中会返回栈溢出的错误,如果一个线程的栈太深会返回此错误。
如下一个例子:
private int count = 0;
public static void main(String[] args) {
TestStack testStack = new TestStack();
try {
testStack.testStack();
} catch (Exception e) {
System.out.println("stack:" + testStack.getCount());
}

}
private void testStack() {
count++;
testStack();
}

public int getCount() {
return count;
}

此时会报栈溢出。java.lang.StackOverflowError,其实此时堆里面可能还够用,但超过了栈的最大深度就报错了。


栈桢
在java虚拟机栈中有一种结构是保存运行的上下文数据的,在栈桢中有如下数据:
局部变量表、操作数栈、动态链接方法、返回地址。如果一个函数传入的参数越多,里面的执行方法局部变量也多话那么栈桢就越大了,理论上递归的次数就越小了。



java本地方法栈:管理调用本地方法的空间,和java虚拟机栈类似,java虚拟机栈是管理java函数调用的,而本地方法栈则管理本地方法调用的,只不过本地方法调用是用c语音去实现的,这个栈也会发生栈溢出。

java堆:
几乎java的所有对象和数组的内存分配都是在java堆中去完成的。在java的堆中分为新生代和老年代。新生代是存放刚刚新生成的对象的,当新生代对象没有被回收幸存下来后,会移动到老年代。

新生代其实有划分3个区域:enden区、survivor space0(s0或是from space)和survivor space1(s1,或是to space区域)enden区是对象的出生地,是刚刚初始化的对象的地方,当幸存活下来后会存放到s0和s1区。也就说经过一次gc回收后,enden区的存活的对象会进入到s0和s1区,当再次经过gc的时候,那么对象就会放入到老年代了 Tenured generation
s0区域和s1区域一样大。


堆的划分如下:


通常eden/s0的比值是8:1,新生代的最大利用空间为新生代的90%
测试使用如下程序:
public static void main(String[] args) {
byte[] b1 = new byte[1024 * 1024 / 2];
byte[] b2 = new byte[1024 * 1024 * 8];
b2 = null;
b2 = new byte[1024 * 1024 * 8];
//System.gc();

}

jvm的运行参数是:
-Xms40m -Xmx40m -Xmn20m -XX:SurvivorRatio=8 -XX:+PrintGCDetails -Xloggc:./gclogs

初始化堆为40m,最大的堆为40m,新生代分配为20m,新生代的eden/s0的比值为8,并打印出详细是gc日志如下:
0.477: [GC 0.478: [ParNew: 10362K->849K(18432K), 0.0040941 secs] 10362K->849K(38912K), 0.0042710 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
Heap
 par new generation   total 18432K, used 9696K [7f8600000, 7f9a00000, 7f9a00000)
  eden space 16384K,  54% used [7f8600000, 7f8ea3e60, 7f9600000)
  from space 2048K,  41% used [7f9800000, 7f98d4498, 7f9a00000)
  to   space 2048K,   0% used [7f9600000, 7f9600000, 7f9800000)
 concurrent mark-sweep generation total 20480K, used 0K [7f9a00000, 7fae00000, 7fae00000)
 concurrent-mark-sweep perm gen total 21248K, used 4735K [7fae00000, 7fc2c0000, 800000000)

上面是没有开启的system.gc的日志。从上可以看出,进行性了一次新生代的gc。
b1变量由eden区域移动到了from区域,后面生成的b2是分配在了eden区域。

开启调用gc的日志如下:

0.303: [GC 0.303: [ParNew: 10362K->846K(18432K), 0.0025869 secs] 10362K->846K(38912K), 0.0027647 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
0.309: [Full GC (System) 0.309: [CMS: 0K->9026K(20480K), 0.0204103 secs] 9365K->9026K(38912K), [CMS Perm : 4677K->4676K(21248K)], 0.0205271 secs] [Times: user=0.05 sys=0.01, real=0.02 secs] 
Heap
 par new generation   total 18432K, used 655K [7f8600000, 7f9a00000, 7f9a00000)
  eden space 16384K,   4% used [7f8600000, 7f86a3df0, 7f9600000)
  from space 2048K,   0% used [7f9800000, 7f9800000, 7f9a00000)
  to   space 2048K,   0% used [7f9600000, 7f9600000, 7f9800000)
 concurrent mark-sweep generation total 20480K, used 9026K [7f9a00000, 7fae00000, 7fae00000)
 concurrent-mark-sweep perm gen total 21248K, used 4734K [7fae00000, 7fc2c0000, 800000000)

如上新生代的被回收了,enden区和from区都回收了,其存活对象被回收到了老年代(红色标记)

方法区:

方法区也是一个重要的java内存区域,和java堆类似也是所有线程共享的,只是方法区存放的是类信息和元数据。(类信息、常量池、域信息、方法信息等)

方法区也被称为永久区、持久代。

Hot spot虚拟机对于常量池的回收是很明确的,只要常量池对象没有被其他地方引用就可以被回收。
下面是测试的代码:
public static void main(String[] args) {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
String t = String.valueOf(i).intern();
}
}
jvm参数:
-XX:MaxPermSize=64m -XX:+PrintGCDetails -Xloggc:./gclogs

这里只是分配了64m的空间,可以看到gc的日志可以正常回收方法区的内存。

244.919: [Full GC 244.919: [CMS: 289K->289K(63872K), 0.3033899 secs] 10165K->289K(83008K), [CMS Perm : 65535K->4713K(65536K)], 0.3034787 secs] [Times: user=0.21 sys=0.07, real=0.31 secs] 
250.831: [GC 250.831: [ParNew: 17024K->2K(19136K), 0.0068389 secs] 17313K->291K(83008K), 0.0069196 secs] [Times: user=0.03 sys=0.00, real=0.00 secs] 

当方法区快要满的时候最终还是被回收了。

注意,如果设置为最大的为4mb,此时会报错。这是因为默认设置应该是:
Heap Configuration:
   MinHeapFreeRatio = 40
   MaxHeapFreeRatio = 70
   MaxHeapSize      = 132120576 (126.0MB)
   NewSize          = 21757952 (20.75MB)
   MaxNewSize       = 87228416 (83.1875MB)
   OldSize          = 65404928 (62.375MB)
   NewRatio         = 7
   SurvivorRatio    = 8
   PermSize         = 21757952 (20.75MB)
   MaxPermSize      = 85983232 (82.0MB)
即是默认的方法区最小是21mb的。可以通过jmap -heap pid来进行查看。














  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值