Java教程

jvm进阶

本文主要是介绍jvm进阶,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

1.随着JIT编译器的发展,在编译期间,如果JIT经过逃逸分析,发现有些对象没有逃逸出方法,那么有可能堆内存分配会被优化成栈内存分配。但是这并不是绝对的。
2.JVM在内存新生代Eden Space中开辟了一小块区域,由线程私有,称作TLAB(Thread-local allocation buffer),默认设定为占用Eden Space的1%。在Java程序中很多对象都是小对象且用过即丢,它们不存在线程共享也适合被快速GC,所以对于小对象通常JVM会优先分配在TLAB上,并且TLAB上的分配由于是线程私有所以没有锁开销。因此在实践中分配多个小对象的效率通常比分配一个大对象的效率要高。
虚拟机是否使用TLAB,可以通过-XX:+/-UseTLAB参数来设定;通常默认的TLAB区域大小是Eden区域的1%,当然也可以手工进行调整,对应的JVM参数是-XX:TLABWasteTargetPercent。

 

 

 

什么情况下会发生栈内存溢出?
是否有递归调用
是否有大量循环或死循环
全局变量是否过多
数组、List、map数据是否过大
使用DDMS工具进行查找大概出现栈溢出的位置
什么情况下会发生堆内存溢出?
是否App中的类中和引用变量过多使用了Static修饰
是否App中使用了大量的递归或无限递归(递归中用到了大量的建新的对象)
是否App中使用了大量循环或死循环(循环中用到了大量的新建的对象)
检查App中是否使用了向数据库查询所有记录的方法。即一次性全部查询的方法,如果数据量超过一定数量,就可能会造成内存溢出。所以在查询时应采用“分页查询”。
检查是否有数组,List,Map中存放的是对象的引用而不是对象,因为这些引用会让对应的对象不能被释放。会大量存储在内存中。
检查是否使用了“非字面量字符串进行+”的操作。因为String类的内容是不可变的,每次运行"+"就会产生新的对象,如果过多会造成新String对象过多,从而导致JVM没有及时回收而出现内存溢出。

线程之间如何通信

https://zhuanlan.zhihu.com/p/51613784

 

8 种基本操作,如下图:

  • lock (锁定) ,作用于主内存的变量,它把一个变量标识为一条线程独占的状态。
  • unlock (解锁) ,作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
  • read (读取) ,作用于主内存的变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的 load 动作使用。
  • load (载入) ,作用于工作内存的变量,它把 read 操作从主内存中得到的变量值放入工作内存的变量副本中。
  • use (使用) ,作用于工作内存的变量,它把工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用到变量的值的字节码指令时就会执行这个操作。
  • assign (赋值) ,作用于工作内存的变量,它把一个从执行引擎接收到的值赋给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
  • store (存储) ,作用于工作内存的变量,它把工作内存中一个变量的值传送到主内存中,以便随后 write 操作使用。
  • write (写入) ,作用于主内存的变量,它把 Store 操作从工作内存中得到的变量的值放入主内存的变量中。

 

内存交互基本操作的 3 个特性

原子性(Atomicity)

可见性(Visibility)

有序性(Ordering)

2、happens-before 关系

介绍系列规则之前,首先了解一下 happens-before 关系:用于描述下 2 个操作的内存可见性。如果操作 A happens-before 操作 B,那么 A 的结果对 B 可见。

happens-before 关系的分析需要分为单线程和多线程的情况:

  • 单线程下的 happens-before,字节码的先后顺序天然包含 happens-before 关系:因为单线程内共享一份工作内存,不存在数据一致性的问题。
    在程序控制流路径中靠前的字节码 happens-before 靠后的字节码,即靠前的字节码执行完之后操作结果对靠后的字节码可见。
    然而,这并不意味着前者一定在后者之前执行。实际上,如果后者不依赖前者的运行结果,那么它们可能会被重排序。
  • 多线程下的 happens-before,多线程由于每个线程有共享变量的副本,如果没有对共享变量做同步处理,线程 1 更新执行操作 A 共享变量的值之后,线程 2 开始执行操作 B,此时操作 A 产生的结果对操作 B 不一定可见。

为了方便程序开发,Java 内存模型实现了下述支持 happens-before 关系的操作:

    • 程序次序规则,一个线程内,按照代码顺序,书写在前面的操作 happens-before 书写在后面的操作。
    • 锁定规则,一个 unLock 操作 happens-before 后面对同一个锁的 lock 操作。
    • volatile 变量规则,对一个变量的写操作 happens-before 后面对这个变量的读操作。
    • 传递规则,如果操作 A happens-before 操作 B,而操作 B 又 happens-before 操作 C,则可以得出操作 A happens-before 操作 C。
    • 线程启动规则,Thread 对象的 start() 方法 happens-before 此线程的每个一个动作。
    • 线程中断规则,对线程 interrupt() 方法的调用 happens-before 被中断线程的代码检测到中断事件的发生。
    • 线程终结规则,线程中所有的操作都 happens-before 线程的终止检测,我们可以通过 Thread.join() 方法结束、Thread.isAlive() 的返回值手段检测到线程已经终止执行。
    • 对象终结规则,一个对象的初始化完成 happens-before 它的 finalize() 方法的开始。
    • 3、内存屏障

      Java 中如何保证底层操作的有序性和可见性?可以通过内存屏障。

      内存屏障是被插入两个 CPU 指令之间的一种指令,用来禁止处理器指令发生重排序(像屏障一样),从而保障有序性的。

      另外,为了达到屏障的效果,它也会使处理器写入、读取值之前,将主内存的值写入高速缓存,清空无效队列,从而保障可见性。

      举个例子说明:

这篇关于jvm进阶的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!