java基础知多少(二)

来源:互联网 发布:淘宝上印度德玛药房 编辑:程序博客网 时间:2024/04/30 12:34

Java基础知识(二)

摘要

通常情况下:了解一些看上去很容易,其实这些东西的背后是很复杂的。想深入了解并熟悉它们,就必需要认识它们背后的知识。

问题:

要了解Java基础知识中的一些看上去很难的知识点,像锁,同步,之类的。想要很容易的了解为什么?需要了解JVM(Java虚拟机),在JVM中有很多的知识点可以轻松的解释JavaSE中的难点。

先来了解一下JVM,VM是Virtual Machine,这和我们平时说的虚拟机(这里指装载操作系统的虚拟机)有什么不同。平时说的VM,是建立在物理硬件上的,而JVM是建立在软件上的,因此会有不同。

在学习Java的过程中内存溢出和内存泄露,内存溢出。引用以下内容来解释它们之间的不同之处:

引用

内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。

内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。

JVM知识引用

在解释内存泄露的基础原理之前,我们要学习以下JVM的栈(stack),栈是线程私有,栈由一系列帧组成(因此Java栈也叫做帧栈),帧保存一个方法的局部变量、操作数栈、常量池指针,每一次方法调用创建一个帧,并压栈。函数调用组成帧栈,帧栈会自回收自己的资源,一般不会发生内存泄露,因此发生内存泄露是在堆上。堆(heap)和程序开发密切相关,应用系统对象都保存在Java堆中,所有线程共享Java堆,对分代GC来说,堆也是分代的,GC的主要工作区间。内存溢出可以发生在堆,也可能发生在栈上。

这里是一张关于jvm加载变量的流程图,从图片上可以看出主内存就是堆(heap),本地内存是栈(stack),线程将共享变量复制成副本保存在栈上,这时就出现了读写问题。

对于Java中的关键字volatile,我们一般知道volatile 不能代替锁,但是一般认为volatile 比锁性能好(不绝对)。volatile对变量是线程可见的,就是一个线程修改了变量,其他线程可以立即知道。相当于在本地内存A,B中没有保存共享变量的副本,而是直接读写主存中的变量。可以实现线程可见的还有final关键字,synchronized,由此可见这些基本和读写有关。对于锁,同步在jvm中主要是由于jvm在对二进制代码进行优化是使用了指令重排导致的问题给出的解决方案:

线程内串行语义

  • 写后读 a = 1;b = a; 写一个变量之后,再读这个位置。
  • 写后写 a = 1;a = 2; 写一个变量之后,再写这个变量。
  • 读后写 a = b;b = 1; 读一个变量之后,再写这个变量。

注意:以上语句不可重排

编译器不考虑多线程间的语义可重排: a=1;b=2;上面是关于读写重排的逻辑解释。破坏线程间的有序性,因此,Java找到了同步和锁来解决这个问题。下面是指令重排的基本原则:

指令重排的基本原则

  • 程序顺序原则:一个线程内保证语义的串行性。
  • volatile规则:volatile变量的写,先发生于读。
  • 锁规则:解锁(unlock)必然发生在随后的加锁(lock)前。
  • 传递性:A先于B,B先于C 那么A必然先于C。
  • 线程的start方法先于它的每一个动作。
  • 线程的所有操作先于线程的终结(Thread.join())。
  • 线程的中断(interrupt())先于被中断线程的代码。
  • 对象的构造函数执行结束先于finalize()方法。