高级特征(advanced feature)是指各种软件或程序为了实现更为复杂的任务或目标而建立的本身特有的一系列功能特征。
Java高级特征
运行时数据区域
运行时数据区域包括
程序计数器、java虚拟机栈、本地方法栈、java堆和方法区。
程序计数器:当前线程所执行的字节码的行号指示器,改变这个计数器的值来选取下一条需要执行的字节码指令;
java虚拟机多线程是通过线程轮流切换并分配处理器执行时间的方式实现,线程切换后能恢复到正确的执行位置,所以线程私有;native方法的计数器为空
java虚拟机栈:线程私有,
生命周期与线程相同,每个方法被执行的时候会创建一个栈帧用于存储局部变量表、操作栈、动态链接、方法出口等,一个方法调用完成的过程就是从入栈到出栈的过程。一般说的栈就是虚拟机栈中的局部变量表,存放编译期可知的各种基本数据(boolean、byte、char、short、int、float、long、double)、对象引用和returnAdress类型。局部变量表所需的内存空间在编译期间完成分配(完全确定分配多大内存),在运行期间不会改变局部变量表的大小,最小单位Slot,每个slot可重用,虚拟机通过索引定位的方式使用局部变量表
本地方法栈:跟虚拟机栈作用相似,虚拟机栈为
虚拟机执行java方法服务,而本地方法栈则为虚拟机使用的native方法服务,sun hotspot虚拟机直接就把两者合二为一
java堆:被所有线程共享的一块内存区域,在虚拟机启动的时候创建,唯一目的就是存放对象实例;主流虚拟机都是按照可扩展来实现(通过-Xmx和-Xms控制),如果堆中没有内存完成实例分配,并且堆也无法再扩展时将会抛出OutOfMemoryError异常
方法区:线程共享的内存区域,用于存储已被
虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据;有时称为永久代,这个区域的内存回收目标主要针对常量池的回收和对类型的卸载。运行时常量池是方法区的一部分,用于存放编译期生成的各种字面量和符号引用,将在类加载后存放到方法区的运行时常量池中;具备动态性,运行期间也可能将新的常量放入池中(如string类的intren()方法)
主流对象访问方式
主流对象访问方式:使用句柄和直接指针。
使用句柄,java堆中会将划分出一块内存来作为句柄池,reference中存储的就是对象的句柄地址,句柄中包含了对象实例数据(java堆)和类型数据(方法区)各自的具体信息地址;
直接指针,
reference中存储的就是对象地址,节省了一次指针定位时间的开销,速度更快
垃圾收集
垃圾收集:主要指java堆跟方法区,前面三个区域都随线程而生而灭,内存在编译期具备确定性,方法或者线程结束就回收了。
堆一般回收一次70-95%,方法区(永久代)主要回收废弃常量和无用的类
判断对象是否存活:
jdk1.2后分为强引用、软引用、弱引用、虚引用;只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象;软引用描述一些有用但非必需的对象,其关联的对象在系统将要发生内存溢出异常之前列进回收范围之中并进行第二次回收;弱引用也是描述非必需对象,只能生存到下一次垃圾收集发生之前;虚引用对其生存时间不构成影响,也无法通过虚引用取得一个对象实例,为一个对象设置虚引用关联的唯一目的就是希望能在这个对象被收集器回收时收到一个系统通知。
判断一个无用的类
判断一个无用的类:该类所有的实例都已经被回收,java堆中不存在该类的任何引用;加载该类的
ClassLoader已经被回收;该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射该类的方法
垃圾收集算法
标记-清除算法:首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象;缺点:效率低,产生大量不连续内存碎片占有内存。
复制算法:将内存按容量划分大小相等两块,每次只使用其中一块,一块用完后就将还存活的对象复制到另一块,然后再把已使用的内存空间一次清理掉。
缺点:需要更大内存;商业
虚拟机采用此方法回收新生代:分为一块较大的Eden空间和两块小的
Survivor空间,每次使用Eden和其中的一块Survivor,回收时将这两块还存活的对象一次性拷贝到另一块Survivor空间,最后清理掉Eden和刚才用过的Survivor空间;Eden:Survivor大小默认8:1,如果另一块Survivor没有足够的空间就直接通过分配机制进入年老代。
标记-整理:让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存;回收年老代。
分代收集算法:根据对象存活周期的不同将内存划分为几块。
对象优先
对象优先在
Eden分配,当没有足够空间的时候
虚拟机发起一次minor gc。
大对象直接进入老年代,大对象是指需要大量连续内存空间的java对象。
长期存活的对象将进入老年代。
部署程序
高性能硬件上部署程序,主要有两种方式
集群间错误
集群间同步导致的内存溢出
堆外内存导致的溢出错误
外部命令导致系统缓慢
服务器jam进程崩溃
jps:虚拟机进程状况工具,可以列出正在运行的虚拟机进程,并显示虚拟机执行主类的名称
jstat:虚拟机统计信息监视工具,显示本地或远程虚拟机进程中的类装载、内存、垃圾收集、jit编译等运行数据
jinfo:java配置信息工具,实时查看和调整虚拟机各项参数
jmap:java内存映像工具,用于生成堆转储快照(
dump文件),还可以查询finalize执行队列,java堆和永久代的信息
jhat:虚拟机堆转储快照分析工具
jstack:java堆栈跟踪工具,生成虚拟机当前时刻的线程快照,用于定位线程出现长时间停顿的原因
加载和连接
java中类型的加载和连接过程都是在程序运行期间完成的
类的整个生命周期:加载、验证、准备、解析、初始化、使用、卸载
加载:
验证:连接阶段的第一步,为了确保Class文件中的字节流中包含的信息符合当前虚拟机的要求,且不会危害虚拟机自身安全。
文件格式验证、元数据验证、字节码验证和符号引用验证四个阶段。
准备:正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中进行分配(不包括实例变量的初始化,它将在对象实例化的时候分配在java堆中)。
解析:虚拟机将常量池内的符号引用替换为直接引用的过程
初始化:执行类构造器()方法的过程
类加载器
类加载器:启动类加载器、扩展类加载器、应用程序类加载器
执行引擎
编译过程
javac编译过程:解析与填充符号表过程;插入式注解处理器的注解处理过程;分析与字节码生成过程
解析与填充符号表:
1)词法语法分析:词法分析是将源代码的字符流转变为标记(
Token)集合,语法分析是根据Token序列来构造
抽象语法树的过程;
2)填充符号表:符号表由一组符号地址和符号信息构成的表格,可用于语义检查,地址分配的依据
注解处理器:在处理注解期间对语法树进行修改,编译器将回到解析及填充符号表的过程重新处理
语义分析与
字节码生成:语义分析的主要任务是对结构上正确的源程序进行上下文有关性质的审查,分为标注检查和数据及控制流分析两个步骤;通常使用语法糖能够增加程序的可读性,减少代码出错的机会;字节码生成是java编译过程最后一个阶段,把语法树、符号表等转化成字节码写到磁盘中
解释器和编译器
当程序需要迅速启动和执行的时候,
解释器可以首先发挥作用,省去编译的时间,立即执行;当程序运行后,随着时间推移,编译器逐渐发挥作用,把越来越多的代码编译成本地代码之后,可以获取更高的执行效率。
hotspot虚拟机内置量两个
即时编译器client compiler和server compiler,采用分层编译的策略。
“热点代码”会被即时编译器编译:被多次调用的方法;被多次执行的循环体。热点探测:基于采样;基于计数器(方法调用计数器,回边计数器)。
即时编译器编译优化技术
1)方法内联:除去方法调用成本,为其他优化建立良好基础
2)进行冗余访问消除
3)复写传播
4)无用代码消除
5)公共子表达式消除
6)数组边界检查消除
7)逃逸分析
Java内存模型
Java内存模型:屏蔽各种硬件和操作系统的内存访问差异,以实现让java程序在各种平台下都能达到一致的并发效果。
主内存与工作内存:每条线程的工作内存保存了该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中进行
内存间交互操作
内存间交互操作:lock(锁定)、unlock(解锁)、read(读取)、load(载入)、use(使用)、assign(赋值)、store(存储)、write(写入)
volatile:一个变量定义成
volatile之后,具备两种特性,第一是保证此变量对所有线程的可见性;但并发下并一定是安全的,因为java里面的运算操作并非原子操作,由于
volatile变量只能保证可见性,在不符合以下两条规则的运算场景中,仍需要通过加锁(使用synchronized或java.util.concurrent中的原子类)来保证原子性。
1)运算结果并不依赖变量的当前值,或者确保只有单一的线程修改变量值
2)变量不需要与其他状态变量共同参与不变约束
第二个特性是禁止指令重排序优化。
volatile变量的读操作的性能消耗与普通变量几乎没啥差别,但是写操作可能会慢一些,因为需要在本地代码中插入许多内存屏障指令来保证处理器不发生乱序执行,在volatile与锁中选择的唯一判断依据仅仅是volatile的语义能否满足使用场景的需求
允许
虚拟机将没有被volatile修饰的64位数据的读写操作划分为两次32位的操作来进行,即允许虚拟机实现选择可以不保证64位数据类型的load、store、read、write这四个操作的原子性,这就是所谓的long或double的非原子性协定,目前商用虚拟机几乎把64位数据的读写操作作为原子操作来对待
java内存模型围绕在并发过程中如何处理原子性、可见性和有序性三个特性来建立的。
可见性:(volatile、synchronized和final修饰)
有序性:线程内表现为串行的语义,观察另一个线程则是无序的
线程的实现
线程的实现主要有三种方式:使用内核线程实现,使用用户线程实现,使用用户线程加轻量级进程混合实现
一条java线程就映射到一条轻量级进程之中,线程调度是指系统为线程分配处理器使用权的过程,主要调度
方式有两种:协同式线程调度和抢占式线程调度,java使用抢占式,java线程是被映射到操作系统的原生线程上来实现的,线程调度最终还是由操作系统说了算
java进程5个状态,任一时刻有且只有一种状态:新建、运行、无限期等待、限期等待、
阻塞、结束
java中各种操作共享的数据分为五类:不可变、绝对线程安全、相对线程安全、线程兼容和线程对立
线程安全的实现方法:互斥同步(悲观)、非阻塞同步(乐观)、无同步方案
锁优化
1)自旋锁与自适应自旋
2)锁消除
3)锁粗化
4)轻量级锁
5)偏向锁
Beand高级特征
Spring程序中,Java Bean一般与Spring是非耦合的,不会依赖于
Spring类库。这也是Spring的优点。
实现BeanNameAware接口获取本bean的id属性
BeanNameAware中一个方法setBeanName(String arg0);它会在bean所有参数设置后 init-method之前调用,在bean中声明一个属性接受。
获取BeanFactory
实现BeanFactoryAware可以获取该bean对应的BeanFactory。
demo:
View Code
实现DisposableBean接口执行销毁方法。
被bean中的destory-method属性替代,一般不使用
实现InitializingBean接口执行初始化方法。
被bean的init-method属性替代,一般不使用
BeanFactory对象
BeanFactory对象的几个常用的方法:
boolean containsBean(String):判断指定名称的Bean是否存在
Object getBean(String):返回指定名称的Bean。如果没有,会抛出异常
Object getBean(String,Class):返回指定名称的Bean,并进行类型转换成类对象。两个过程中出错都会异常
boolean isSingleton(String):判断指定名称的
Bean是否配置为singleton。没有bean,抛出异常
String[] getAliases(String):获取指定名称bean的所有别名的数组
属性覆盖器
对于一些参数,更实用更简单的方法是使用properties配置,而不是配置在Spring的配置文件中。Spring提供属性替代属性,允许把某些属性配置在properties文件中。
配置PropertyOverrideConfigurer属性覆盖器
PropertyOverrideConfigure允许把
XML配置里的魔偶写参数配置到properties文件中。这些在数据库配置中很常用。配置时需要配置一个PropertyOverrideConfigure对象,指定properties文件的位置,然后把替换的属性用形如的字符串替代。
Pro/ENGINEER高级特征
Pro/
ENGINEER的高级特征实现了复杂曲面的数字化表示,使难以描述的复杂模型变得直观明了而且非常精确。利用Pro/ENGINEER的零件高级
建模功能可以设计外形复杂的产品,从机械产品中的弹簧、螺纹到工业造型设计中的手机外壳、
显示器等,都离不开高级特征的使用。可以说,Pro/ENGINEER的高级特征对工业造型设计人员来说是必不可少的得力助手,它大大提高了产品设计的效率和质量。
螺旋扫描
螺旋扫描,即用一个截面沿着螺旋轨迹扫描来创建特征,它通过旋转曲面的轮廓和螺距两者来定义轨迹。螺旋轨迹和旋转曲面只是一种作图工具,它们不会出现在生成的特征中。
螺旋扫描功能可用于实体伸出、实体切口、薄壳伸出和切口,以及曲面及曲面修剪等特征。从主菜单〖插入〗|〖螺旋扫描〗|〖伸出〗,在〖属性〗菜单中,对成对出现的命令(只能选择其一)进行选择来定义螺旋扫描特征。
〖常量〗螺距为常数。
〖变量〗螺距是可变的,并由某图形定义。
〖穿过轴〗横截面位于穿过旋转轴的平面内。
〖轨迹法向〗确定横截面方向,使之垂直于轨迹或旋转面。
〖右手定则〗使用右手定则定义轨迹,即右旋。
〖左手定则〗使用左手定则定义轨迹,即左旋。
可变剖面扫描
可变剖面扫描允许截面沿所选的多条轨迹运动来创建特征,截面在运动过程中其形状沿着
轨迹线变化。多条轨迹线中,一条
轨迹线控制截面的运动路线,其他几条轨迹线控制截面的形状,截面在运动过程中,并一定垂直于轨迹线。
使用可变剖面扫描功能,必须先绘制轨迹线。然后从主菜单〖插入〗|〖变剖面扫描〗,或从工具栏中选择按钮,弹出可变剖面扫描操控板。可进行实体的伸出和切除、曲面及修剪、薄壳等特征。
〖参照〗用于选取轨迹线及对截面运动的控制方式,点击弹出上滑面板。
扫描混合命令
扫描混合命令使用一条轨迹线与几个剖面来创建一个实体特征,同时具有扫描和混合的效果。
选择
主菜单〖插入〗|〖螺旋扫描〗|〖伸出〗,出现图7.28所示的菜单管理器。 扫描混合的截面放置方式与可变截面扫描相似:
垂直于原始轨迹:截面垂直于原始轨迹线上该截面放置点的切矢量,即确定Z轴。
轴心方向:截面垂直于原始轨迹线,并沿指定的方向扫描。