《Java特种兵》学习笔记java
String a = "a" + "b" + 1;
String b = "ab1";
println(a == b); // true 编译期优化
复制代码
String a = "a";
final String c = "a";
//a并非一个常量,而是局部变量,字节码加强技术就能够修改a的实际赋值
String b = a + "b";
String d = c + "b"; //final
//1.编译器不会看方法内部作了什么,2.递归深度不可预测
//3.返回的是对常量引用的拷贝,不是final的,能够修改
String e = getA() + "b";
String compare = "ab";
println(b == compare); //fasle
println(d == compare); //true
println(e == compare); //fasle
private final static String getA() {
return "a";
}
复制代码
//String b = a + "b" 的实际编译效果
StringBuilder temp = new StringBuilder();
String b = temp.append(a).append("b");
复制代码
public native String intern();
与string pool关联,加锁寻找string字符串,用equals方法判断是不是目标字符串
Jdk1.6: string pool在 Perm Gen中
Jdk1.7:string pool在堆中mysql
public static void test3() {
String a = "a";
String b = a + "b"; //a是变量,new StringBuilder().append(a).append("b");
String c = "ab"; //在string pool中新建ab字符串
String d = new String(b);//新对象
println(b == c); //F
println(c == d); //F
println(c == d.intern()); //True:intern:寻找string pool中的ab,返回地址,没有则建立后返回地址
println(b.intern() == d.intern()); //True:intern:寻找string pool中的ab,返回地址,没有则建立后返回地址
}
复制代码
老年代是否能够独自清理?而新生代不清理?web
StringBuilder stringBuilder = new StringBuilder();
for (...) {
//最坏的状况是它所占用的内存空间接近Old区域1/3时发生扩容,致使OOM
stringBuilder.append(string);
}
复制代码
stringBuilder在append扩容的时候,取max(2倍,count+string.length)
,因此在小字符串append大字符串时,扩容的空间刚刚足够,而大字符串append小字符串时,就会多出大量的内存空间算法
举例:java字节码中的类修饰符,如下都是十六进制,只取后四位显示spring
public:0001
static:0100
final:1000
复制代码
对数字取&操做,不为0时就是true
若判断 public static final
, 先取或 public_static_final=public|static|final
;判断(value& public_static_final)== public_static_final
sql
类型 | Cache范围 |
---|---|
Integer | -128 ~127 |
Short | -128 ~127 |
Long | -128 ~127 |
Float double | 无 |
Byte | 256个值 |
存储局部变量中的基本数据类型,新建对象时,储存对象引用,而对象是在堆中建立
Jvm发出指令请求,OS完成具体计算,jvm自身没法作计算数据库
一般以连续64位字节为单位进行cache的
如数组获取,当取二维数组a[0][0]时,cache line操做一般会将一些临近的数组元素cache到CPU缓存中,故而连续访问a[0][0],a[0][1]…时,这些连续的数值只要cache一次数组
同一份数据cache在多个cpu中时,要求数据读写一致,多个CPU之间要遵循缓存共享的一致性协议
CPU读写数据时都会广播,其余CPU监听,并保存数据一致性缓存
全部程序中使用的地址都是虚拟地址(逻辑地址),在不一样的进程中能够重复
物理地址:每一个进程有一段内存区域,起始地址+逻辑地址=物理地址
OS预先给jvm分配-xms大小的内存空间,而不是当即分配一个-xmx大小的空间,许多空间是真正使用时才分配的(启动java时,-xmx设置比物理内存大均可以)安全
每秒读取的次数IOPS越大越好
顺序读写,减小定位延迟
Java中的日志读写工具,会将日志替换为buffer的append方式,将日志写入一个缓冲区,由专门的程序实现写操做,或者只在缓冲区已满的时候写入磁盘,尽可能一次写入多条数据,顺序IO,减小硬盘寻道寻址
类常量池
方法:编译时会自动生成构造方法字节码
略过先
ClassLoader.loadClass(“类名”)
时,会先从当前ClassLoader查找该类是否已经加载,而后逐步往父类查找类,最后由父类往子类加载类,最后ClassNotFoundExceptionBootStrapClassLoader -> ExtClassLoader -> AppClassLoader -> 自定义ClassLoader
java.Lang.*( Object, Class, Number, Thread, System, Throwable…)
,由jvm内核实现,不能被替换掉全部类在使用前都必须被加载和初始化,初始化过程由<clinit>
方法确保线程安全,若多个线程同时尝试获取该类,则必须等到static块执行完成
(BootStrapClassLoader -> ExtClassLoader -> AppClassLoader ->自定义ClassLoader->classnotfoundexception)
static块 -> 代码块 -> 构造方法
class Parent {
public Parent() {
System.out.println("parent constructor init...."); //4
}
static {
System.out.println("parent static block init...."); //1
}
{
System.out.println("parent normal block call...."); //3
}
}
class Child extends Parent {
static {
System.out.println("child static block call...."); //2
}
{
System.out.println("child block call...."); //5
}
public Child() {
System.out.println("child constructor call...."); //6
}
}
public class LoadObjectStepDemo {
public static void main(String[] args) {
new Child();
}
}
复制代码
parent static block init....
child static block call....
parent normal block call....
parent constructor init....
child block call....
child constructor call....
复制代码
错误初始化实例ExceptionInInitializerError
class B {
// 加载类时先调用static块
private final static B instance = new B();
public static B getInstance() {
return instance;
}
public B() {
instance.test(); // new B()引用instance实例,但又发现这个类未加载完成,instance为NULL
}
public void test() {
System.out.println("test");
}
}
复制代码
-XX:ReservedCodeCacheSize
: 修改codeCache大小,64bit server java7默认48M-XX:+UseCodeCacheFlushing
: 清理codeCache-XX:CICompilerCount
: 最大并行编译数,越大提升编译速度instance.getClass().getResource("").getPath();
得到class来源jar包见jvm笔记
跟 Java 堆大小相关的 JVM 内存参数
参数 | 含义 |
---|---|
-Xms | 设置 Java 堆的初始化大小 |
-Xmx | 设置最大的 Java 堆大小 |
-Xss | 设置Java线程堆栈大小 |
-Xmn | 设置新生代空间大小 |
关于打印垃圾收集器详情的 JVM 参数
参数 | 含义 |
---|---|
-verbose:gc | 记录 GC 运行以及运行时间,通常用来查看 GC 是不是应用的瓶颈 |
-XX:+PrintGCDetails | 记录 GC 运行时的详细数据信息,包括新生成对象的占用内存大小以及耗费时间等 |
-XX:-PrintGCTimeStamps | 打印垃圾收集的时间戳 |
设置 Java 垃圾收集器行为的 JVM 参数
参数 | 含义 |
---|---|
-XX:+UseParallelGC | 使用并行垃圾收集 |
-XX:-UseConcMarkSweepGC | 使用并发标志扫描收集 |
-XX:-UseSerialGC | 使用串行垃圾收集 |
JVM调试参数,用于远程调试
-Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000
复制代码
-Xbootclasspath
用来指定须要加载,但不想经过校验的类路径。JVM 会对全部的类在加载前进行校验并为每一个类经过一个int数值来应用。这个是保证 JVM 稳定的必要过程,但比较耗时,若是但愿跳过这个过程,就把类经过这个参数来指定。java.lang.OutOfMemoryError:Perm Gen Space.
-XX:PermSize and -XX:MaxPermSize
-XX:NewRatio=2 Ratio of new/old generation sizes.
-XX:MaxPermSize=64m Size of the Permanent Generation.
复制代码
-XX:+TraceClassLoading
和 -XX:+TraceClassUnloading
用来打印类被加载和卸载的过程信息,这个用来诊断应用的内存泄漏问题很是有用。-XX:+PrintCompilation
: prints out the name of each Java method Hotspot decides to JIT compile.参数 | 含义 |
---|---|
-XX:HeapDumpPath=./java_pid.hprof | Path to directory or file name for heap dump. |
-XX:-PrintConcurrentLocks | Print java.util.concurrent locks in Ctrl-Break thread dump. |
-XX:-PrintCommandLineFlags | Print flags that appeared on the command line. |
Java对象将以8字节对齐在内存中,不足则补齐
静态引用所占的空间一般不计算到对象空间自己的空间上,它的引用在方法区
//32bit
class A{
byte b1;
}
复制代码
8字节头部+1字节b1
要对齐,故16字节
class A{byte b;}
class B extends A{byte b;}
class C extends B{byte b;}
复制代码
int size = 100 * 1024 * 1024;
//1
int[] values = new int[size];
for (int i = 0; i < size; i++) {
values[i] = i;
}
//2
Integer[] valueIntegers = new Integer[size];
for (int i = 0; i < size; i++) {
valueIntegers[i] = i; // 自动装箱了 new Integer(i)
}
复制代码
维度 | Int[2][100] | int[100][2] | |
---|---|---|---|
第一维数组 | 对象头部 | 8 | 8 |
第一维 | 数组长度描述符 | 4 | 4 |
第一维 | 引用宽度 | 2X4=8 | 100X4=400 |
第一维 | Padding | 4 | 4 |
第一维 | 合计 | 24 | 416 |
第二维 | 对象头部 | 8 | 8 |
第二维 | 数组长度描述符 | 4 | 4 |
第二维 | 引用宽度 | 100X4=400 | 2X4=8 |
第二维 | Padding | 4 | 4 |
第二维 | 合计 | 416 | 24 |
总计 | 24+2X416=856 | 416+100X24=2816 |
java.lang.OutOfMemoryError: Java heap space
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
while (true) {
list.add("内存溢出了");
}
}
复制代码
java.lang.OutOfMemoryError: PermGen space
int i = 0;
while (true) {
("在JDK 1.6下运行,在JDK 1.7中运行的结果将彻底不一样 "
+ "string常量在jdk1.7以上就再也不存放在PermGen中" + i++).intern();
}
复制代码
java.lang.OutOfMemoryError: Direct buffer memory
// -XX:MaxDirectMemorySize=26m
public static void main(String[] args) {
ByteBuffer.allocateDirect(27 * 1024 * 1024);
}
复制代码
public void testStackOver() {
testStackOver();
}
复制代码
注意递归层数,将递归调用次数做为参数,到达必定次数后结束递归若字符编码和解码的方式不一致,极可能损坏源字符,形成没法正确读取
好比变长的UTF-8编码能够由3个字节组成一个汉字,而GBK由2个字节组成汉字,GBK按2个单位 长度读取时,不在其编码范围内的则用?或其余字符替代,这就修改了原来的字符串了
不关闭流会有什么问题?内存溢出吧
select * from table small,table big where small.id =big.id and small.type=’1’;
复制代码
small.id =big.id
:用小表作驱动表,更快update table set … where a=xx and b=xxxx
复制代码
若a有索引,而b没有,则可能会锁住全部a条件筛选出的数据,锁的范围更大 有些数据库为了加快加锁的速度,会以”块”为单位加锁,块头部有内部的行信息,每一行都有1bit来标识是否已经加锁索引方法:B+树,也有hash
索引是与数据分开储存的
索引的目的是快速定位数据,索引经过某种标识符与原表关联起来(也叫回表),标识符能够是主键,或者创建索引的字段值,也能够是数据物理位置(oracle用rowid作标识符:表空间编号+文件编号+对象编号+块号+块内行号
)
除非查询的信息所有在索引上,不然索引至少会走两次操做(索引+回表),因此小规模数据索引表现很差
索引更新时,索引先删除再插入,其中删除是伪删除,索引空间不会变小
从新编译索引才会真正删除索引
索引管理
树状管理索引,数据量越大,层数越多,索引间有序,底层全部的叶子块经过双向链表互相关联
字段有索引并不必定要走索引
in检索时,一般解析为OR方式完成,检索在多个离散块中进行,每一个条件都须要单独查找,in条件多就走全表扫描(将in改成exists?)
再如,某些状态字段做为条件,可能也不会走索引
位图索引bitMap
但字段只有几种值时,bitmap能够实现高效的统计.
位图索引结构相似于B+树,储存时以字段值类型分开,在每种值的空间中单独储存每一个数据行是否有这个值,1有,0没有
如字段类型为A,索引中大体能够储存为101010100,表示第1 3 5 7行有A值
缺点:锁粒度太大
若修改状态1为2,则会锁住全部值为1和2的行,commit或rollback才会释放
主库更新数据,再同步到从库
小表作驱动表,大表走索引
explain plan for sql,select * from table(DBMS_XPLAN.DISPLAY)
SET AUTOTRACE ON EXPLAIN , SET AUTOTRACE ON, SET AUTOTRACE TRACEONLY STAT
explain plan for select * from tableAA a where a.tt=:vv1;
:开头表明占位符Mysql
explan<sql>
执行计划
在JOIN表以前,须要被JOIN的表能先过滤掉大部分数据
小表驱动大表,嵌套循环时,有序会快一些
多个结果集JOIN,可使用Hash Join的方式,当表太大,Hash Join key太多,内存会放不下,用普通嵌套方式作
函数转换
函数转换一般不属于执行计划的范畴
select fun1(a),fun(b)… from table
执行路径是看不到函数的处理
表示树状结构,如省,市,区县,镇,乡,村,门牌,多级结构,可使用经常使用的递归方式,只保留父级id 也能够按序保留下全部的父级id,避免递归,索引也方便
Thread.currentThread().getStackTrace();
复制代码
new Exception().printStackTrace();
复制代码
Boolean类型属性名称仍是不要加is前缀
class Node {
private boolean good;
public boolean isGood() {
return good;
}
public void setGood(boolean good) {
this.good = good;
}
}
复制代码
当使用PropertyDescriptor去获取属性读写方法时,boolean类型默认都会加上is前缀
若属性名为isGood,则默认调用isIsGood方法,这时类中isGood方法就无论用了
List<Integer> list = new ArrayList<Integer>();
Method method = ArrayList.class.getDeclaredMethod("add",Object.class);
method.invoke(list, 7);
method.invoke(list, "dfsd");
method.invoke(list, new A("aa"));
System.out.println(list);
复制代码
面向切面,动态代理和字节码加强技术
注解根本上仍是依靠反射实现的
驱动只需加载一次,不须要反复加载,也不须要本身new,注册的driver以列表的形式保存,驱动加载也与classloader有关,全部也能够在不一样的classloader加载同一驱动的不一样版本 当程序中存在多个驱动时,DriverManager经过遍历的方式查找其classloader中全部的driver,与jdbcurl匹配,匹配上就返回