java 笔面试第二弹

作者: 进击的小鹿 | 来源:发表于2016-10-20 14:58 被阅读136次

    目录##

    1.内存屏障
    2.关键字native
    3.伪共享
    4.线程Dump文件
    5.内存溢出
    6.jvm 配置和参数

    1.内存屏障##

    目前有4种屏障----

    LoadLoad屏障:对于这样的语句Load1; LoadLoad; Load2,在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。

    StoreStore屏障:对于这样的语句Store1; StoreStore; Store2,在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。

    LoadStore屏障:对于这样的语句Load1; LoadStore; Store2,在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕。

    StoreLoad屏障:对于这样的语句Store1; StoreLoad; Load2,在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。它的开销是四种屏障中最大的。在大多数处理器的实现中,这个屏障是个万能屏障,兼具其它三种内存屏障的功能。

    2.native##

    "A native method is a Java method whose implementation is provided by non-java code."

    java中,native关键字说明其修饰的方法是一个原生态方法,方法对应的实现不是在当前文件,而是在用其他语言(如C和C++)实现的文件中。Java语言本身不能对操作系统底层进行访问和操作,但是可以通过JNI接口(Java Native Interfac)调用其他语言来实现对底层的访问。存在的必要:需要与java外的环境做交互,与jvm,操作系统交互得到更多的特性,sun的解释器是C实现的,实现更好的交互。

    1. 标识符native可以与所有其它的java标识符连用,但是abstract除外
    2. 一个native method方法可以返回任何java类型,包括非基本类型,而且同样可以进行异常控制。
    3. 使用本地方法是有开销的

    **JVM怎样使Native Method跑起来: **

    当一个类第一次被使用到时,这个类的字节码会被加载到内存,并且只会回载一次。在这个被加载的字节码的入口维持着一个该类所有方法描述符的list,这些方法描述符包含这样一些信息:方法代码存于何处,它有哪些参数,方法的描述符(public之类)等等。

    如果一个方法描述符内有native,这个描述符块将有一个指向该方法的实现的指针。这些实现在一些DLL文件内,但是它们会被操作系统加载到java程序的地址空间。当一个带有本地方法的类被加载时,其相关的DLL并未被加载,因此指向方法实现的指针并不会被设置。当本地方法被调用之前,这些DLL才会被加载,这是通过调用java.system.loadLibrary()实现的。

    JNI的书写步骤如下:

    a.编写带有native声明的方法的Java类

    b.使用javac命令编译编写的Java类

    c.使用java -jni ****来生成后缀名为.h的头文件

    d.使用其他语言(C、C++)实现本地方法

    e.将本地方法编写的文件生成动态链接库

    注:
    1.在C++中,你可以用extern "C"告知C++编译器去调用一个C的函数。
    2.http://blog.csdn.net/wike163/article/details/6635321

    3.伪共享##

    伪共享(false sharing),就是多个线程同时修改共享在同一个缓存行里的独立变量,无意中影响了性能。

    伪共享发生的过程

    当核心1上的线程想更新X,而核心2上的线程想更新Y,而X变量和Y变量在同一个缓存行中时;每个线程都要去竞争缓存行的所有权来更新变量。如果核心1获得所缓存行的所有权,那么缓存子系统将会使核心2中对应的缓存行失效,反之亦然。这会来来回回的经过L3缓存,大大影响了性能。这种情况,就像多个线程同事竞争锁的所有权一样。如果互相竞争的核心位于不同的插槽,就要额外横跨插槽连接,问题可能更加严重。

    典型的CPU微架构有3级缓存, 每个核都有自己私有的L1, L2缓存.

    解决伪共享的方法是通过补齐(Padding),使得每一条缓存行只存一个多线程变量

    参考:
    http://www.it165.net/pro/html/201403/11104.html
    http://coderplay.iteye.com/blog/1486649

    4.线程Dump文件##

    Dump文件是进程的内存镜像。可以把程序的执行状态通过调试器保存到dump文件中。
    Dump文件是用来给驱动程序编写人员调试驱动程序用的,这种文件必须用专用工具软件打开,比如使用WinDbg打开。

    线程dump出来的信息包含线程基本信息;线程的运行状态、标识和调用的堆栈;调用的堆栈包含完整的类名,所执行的方法,如果可能的话还有源代码的行数。因此,可以使用线程Dump文件来进行诊断一些问题, 包括线程阻塞,CPU 使用率过高,JVM Crash,堆内存不足和类装载等。

    输出的方法:

    win下,在启动程序的控制台里敲: Ctrl - Break,线程的 dump会产生在标准输出中

    Linux 下,你可以通过命令 kill -3 PID (Java 进程的进程 ID)来获取 Java 应用的 dump 文件。

    5.内存溢出##

    1.什么是内存溢出

    内存溢出是指应用系统中存在无法回收的内存或使用的内存过多,最终使得程序运行要用到的内存大于虚拟机能提供的最大内存。

    jvm管理的内存大致包括三种不同类型的内存区域:Permanent Generation space(永久保存区域)、Heap space(堆区域)、Java Stacks(Java栈)。
    永久保存区域主要存放Class(类)和Meta的信息,Class第一次被Load的时候被放入PermGen space区域,Class需要存储的内容主要包括方法和静态属性。
    堆区域用来存放Class的实例(即对象),对象需要存储的内容主要是非静态属性。每次用new创建一个对象实例后,对象实例存储在堆区域中,这部分空间也被jvm的垃圾回收机制管理。
    而Java栈跟大多数编程语言包括汇编语言的栈功能相似,主要基本类型变量以及方法的输入输出参数。Java程序的每个线程中都有一个独立的堆栈。容易发生内存溢出问题的内存空间包括:Permanent Generation space和Heap space。

    2.引起内存溢出的主要原因

    1.内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
    2.集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
    3.代码中存在死循环或循环产生过多重复的对象实体;
    4.使用的第三方软件中的BUG;
    5.启动参数内存值设定的过小

    3.解决内存溢出的方法

    第一步,修改JVM启动参数,直接增加内存。(-Xms,-Xmx参数一定不要忘记加。)
    第二步,检查错误日志,查看“OutOfMemory”错误前是否有其它异常或错误。
    第三步,对代码进行走查和分析,找出可能发生内存溢出的位置。

    4.三种类型

    ** OutOfMemoryError: PermGen space**

    发生这种问题的原意是程序中使用了大量的jar或class,使java虚拟机装载类的空间不够,与Permanent Generation space有关。解决这类问题有以下两种办法:

    1. 增加java虚拟机中的XX:PermSize和XX:MaxPermSize参数的大小,其中XX:PermSize是初始永久保存区域大小,XX:MaxPermSize是最大永久保存区域大小。如针对tomcat6.0,在catalina.sh 或catalina.bat文件中一系列环境变量名说明结束处(大约在70行左右) 增加一行:
      JAVA_OPTS=" -XX:PermSize=64M -XX:MaxPermSize=128m"

    2. 清理应用程序中web-inf/lib下的jar,如果tomcat部署了多个应用,很多应用都使用了相同的jar,可以将共同的jar移到tomcat共同的lib下,减少类的重复加载。

    OutOfMemoryError: Java heap space

    发生这种问题的原因是java虚拟机创建的对象太多,在进行垃圾回收之间,虚拟机分配的到堆内存空间已经用满了,与Heap space有关。解决这类问题有两种思路:

    1. 检查程序,看是否有死循环或不必要地重复创建大量对象。找到原因后,修改程序和算法。

    2. 增加Java虚拟机中Xms(初始堆大小)和Xmx(最大堆大小)参数的大小。如:set JAVA_OPTS= -Xms256m -Xmx1024m

    OutOfMemoryError:unable to create new native thread

    这种错误在Java线程个数很多的情况下容易发生。

    6.jvm 配置和参数##

    -Xms 初始堆大小
    -Xmx 最大堆大小
    -Xmn 年轻代大小
    -Xss 每个线程的堆栈大小
    -XX:参数

    具体的内容参照下面的引文吧。

    堆分配

    JVM初始分配的内存由-Xms指定,默认是物理内存的1/64;JVM最大分配的内存由-Xmx指定,默认是物理内存的1/4。默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制;空余堆内存大于70%时,JVM会减少堆直到 -Xms的最小限制。因此服务器一般设置-Xms、-Xmx相等以避免在每次GC 后调整堆的大小。
    对象的堆内存由称为垃圾回收器的自动内存管理系统回收。

    堆分配

    Eden:存放新生的对象
    Survivor Space:有两个,存放每次垃圾回收后存活的对象
    Old Generation:主要存放应用程序中生命周期长的存活对象

    JVM(采用分代回收的策略),用较高的频率对年轻的对象(young generation)进行YGC,而对老对象(tenured generation)较少(tenured generation 满了后才进行)进行Full GC。这样就不需要每次GC都将内存中所有对象都检查一遍。

    非堆内存分配

    JVM使用-XX:PermSize设置非堆内存初始值,默认是物理内存的1/64;由XX:MaxPermSize设置最大非堆内存的大小,默认是物理内存的1/4。

    GC不会在主程序运行期对PermGen Space进行清理,所以如果你的应用中有很多CLASS(特别是动态生成类,当然permgen space存放的内容不仅限于类)的话,就很可能出现PermGen Space错误。

    参考:
    JVM性能优化
    JVM参数设置、分析
    GC策略&内存申请、对象衰老
    HotSpot VM GC 的种类

    相关文章

      网友评论

        本文标题:java 笔面试第二弹

        本文链接:https://www.haomeiwen.com/subject/jbbfuttx.html