Java知识梳理七

2019-03-10  本文已影响0人  欧阳誉晨曦

一、Java多线程二

1.Java内存模型

       首先,程序计数器 (PC,Program CounterRegister)。在JVM规范中,每个线程都有它自己的程序计数器,并且任何时间一个线程都只有一个方法在执行,也就是所谓的当前方法。程序计数器会存储当前线程正在执行的Java方法的JVM指令地址;或者,如果是在执行本地方法,则是未指定值(undefined)。
       第二, Java虚拟机栈(Java Virtual MachineStack) 。早期也叫Java栈。每个线程在创建时都会创建一个虚拟机栈,其内部保存一个个的栈帧(Stack Frame),对应着一次次的Java方法调用。前面谈程序计数器时,提到了当前方法;同理,在一个时间点,对应的只会有一个活动的栈帧,通常叫作当前帧,方法所在的类叫作当前类。如果在该方法中调用了其他方法,对应的新的栈帧会被创建出来,成为新的当前帧,一直到它返回结果或者执行结束。JVM直接对Java栈的操作只有两个,就是对栈帧的压栈和出栈。栈帧中存储着局部变量表、操作数(operand)栈、动态链接、方法正常退出或者异常退出的定义等。
       第三,堆(Heap)。它是Java内存管理的核心区域,用来放置Java对象实例,几乎所有创建的Java对象实例都是被直接分配在堆上。堆被所有的线程共享,在虚拟机启动时,我们指定的“Xmx”之类参数就是用来指定最大堆空间等指标。理所当然,堆也是垃圾收集器重点照顾的区域,所以堆内空间还会被不同的垃圾收集器进行进一步的细分,最有名的就是新生代、老年代的划分。
       第四,方法区 (Method Area)。这也是所有线程共享的一块内存区域,用于存储所谓的元(Meta)数据,例如类结构信息,以及对应的运行时常量池、字段、方法代码等。由于早期的Hotspot JVM实现,很多人习惯于将方法区称为永久代(Permanent Generation)。Oracle JDK8中将永久代移除,同时增加了元数据区(Metaspace)。
       第五,运行时常量池(Run-Time Constant Pool)。这是方法区的一部分。如果仔细分析过反编译的类文件结构,你能看到版本号、字段、方法、超类、接口等各种信息,还有一项信息就是常量池。Java的常量池可以存放各种常量信息,不管是编译期生成的各种字面量,还是需要在运行时决定的符号引用,所以它比一般语言的符号表存储的信息更加宽泛。
       第六, 本地方法栈(Native Method Stack)。它和Java虚拟机栈是非常相似的,支持对本地方法的调用,也是每个线程都会创建一个。在Oracle Hotspot JVM中,本地方法栈和Java虚拟机栈是在同一块儿区域,这完全取决于技术实现的决定,并未在规范中强制。
       JMM总结,1 从JVM运行时视角来看,JVM内存可分为JVM栈、本地方法栈、PC计数器、方法区、堆;其中前三区是线程所私有的,后两者则是所有线程共有的;2 从JVM内存功能视角来看,JVM可分为堆内存、非堆内存与其他。其中堆内存对应于上述的堆区;非堆内存对应于上述的JVM栈、本地方法栈、PC计数器、方法区;其他则对应于直接内存;3 从线程运行视角来看,JVM可分为主内存与线程工作内存。Java内存模型规定了所有的变量都存储在主内存中;每个线程的工作内存保存了被该线程使用到的变量,这些变量是主内存的副本拷贝,线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量;4 从垃圾回收视角来看,JVM中的堆区=新生代+老年代。新生代主要用于存放新创建的对象与存活时长小的对象,新生代=E+S1+S2;老年代则用于存放存活时间长的对象。

2.如何监控和诊断JVM内存
3.Java常见的垃圾收集器
    -XX:+UseSerialGC
    -XX:+UseConcMarkSweepGC -XX:+UseParNewGC
    -XX:+UseParallelGC

另外,Parallel GC引入了开发者友好的配置项,我们可以直接设置暂停时间或吞吐量等目标,JVM会自动进行适应性调整,例如下面参数:

    -XX:MaxGCPauseMillis=value
    -XX:GCTimeRatio=N // GC时间和用户时间比例 = 1 / (N+1)
4.GC调优的思路

       对于GC调优来说,首先就需要清楚调优的目标是什么?从性能的角度看,通常关注三个方面,内存占用(footprint)、延时(latency)和吞吐量(throughput),大多数情况下调优会侧重于其中一个或者两个方面的目标,很少有情况可以兼顾三个不同的角度。当然,除了上面通常的三个方面,也可能需要考虑其他GC相关的场景,例如,OOM也可能与不合理的GC相关参数有关;或者,应用启动速度方面的需求,GC也会是个考虑的方面。基本的调优思路可以总结为:

5.happens-before原则

       程序顺序原则——一个线程内保证语义的串行性;
       volatile规则——volatile变量的写先发生于读,这保证了volatile变量的可见性;
       锁规则——解锁必然发生在随后的加锁前;
       传递性——a先于b,b先于c,那么a必然先于c;
       线程的start()方法先于它的每一个动作;
       线程的所有操作先于线程的终结;
       线程的中断先于被中断线程的代码;
       对象的构造函数执行、结束先于finalize()方法。

6.Java程序在Docker上运行有哪些问题?
7.Java注入攻击

       注入式(Inject)攻击是一类非常常见的攻击方式,其基本特征是程序允许攻击者将不可信的动态内容注入到程序中,并将其执行,这就可能完全改变最初预计的执行过程,产生恶意效果。下面是几种主要的注入式攻击途径,原则上提供动态执行能力的语言特性,都需要提防发生注入攻击的可能。
       首先,就是最常见的SQL注入攻击。一个典型的场景就是Web系统的用户登录功能,根据用户输入的用户名和密码,我们需要去后端数据库核实信息。假设应用逻辑是,后端程序利用界面输入动态生成类似下面的SQL,然后让JDBC执行。

    Select * from use_info where username = “input_usr_name” and password = “input_pwd”

       但是,如果我输入的input_pwd是类似下面的文本,

    “ or “”=”

       那么,拼接出的SQL字符串就变成了下面的条件,OR的存在导致输入什么名字都是复合条件的。

    Select * from use_info where username = “input_usr_name” and password = “” or “” = “”

       这里只是举个简单的例子,它是利用了期望输入和可能输入之间的偏差。上面例子中,期望用户输入一个数值,但实际输入的则是SQL语句片段。类似场景可以利用注入的不同SQL语句,进行各种不同目的的攻击,甚至还可以加上“;delete
xxx”之类语句,如果数据库权限控制不合理,攻击效果就可能是灾难性的。
       第二,操作系统命令注入。Java语言提供了类似Runtime.exec(…)的API,可以用来执行特定命令,假设我们构建了一个应用,以输入文本作为参数,执行下面的命令:

    ls –la input_file_name

       但是如果用户输入是 “input_file_name;rm –rf/”,这就有可能出现问题了。当然,这只是个举例,Java标准类库本身进行了非常多的改进,所以类似这种编程错误,未必可以真的完成攻击,但其反映的一类场景是真实存在的。
       
第三,XML注入攻击。Java核心类库提供了全面的XML处理、转换等各种API,而XML自身是可以包含动态内容的,例如XPATH,如果使用不当,可能导致访问恶意内容。还有类似LDAP等允许动态内容的协议,都是可能利用特定命令,构造注入式攻击的,包括XSS(Cross-site Scripting)攻击,虽然并不和Java直接相关,但也可能在JSP等动态页面中发生。

上一篇下一篇

猜你喜欢

热点阅读