代码之旅

I love Coding !

OOM(OutOfMemoryError)是java工程师都会了解的一种异常,实质上,OOM并不是只有一种,一共有9种不同类型的OOM:

  • java.lang.OutOfMemoryError: Java heap space
  • java.lang.OutOfMemoryError: GC Overhead limit exceeded
  • java.lang.OutOfMemoryError: Permgen space
  • java.lang.OutOfMemoryError: Metaspace
  • java.lang.OutOfMemoryError: Unable to create new native thread
  • java.lang.OutOfMemoryError: reason stack_trace_with_native_method
  • java.lang.OutOfMemoryError: Requested array size exceeds VM limit
  • java.lang.OutOfMemoryError: Kill process or sacrifice child
  • java.lang.OutOfMemoryError: Direct buffer memory

不同的原因触发不同类型的OOM,每种OOM类型的解决方案也不同。

阅读全文 »

正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。

阅读全文 »

假设我们正在编写一个剪刀-布-石头游戏。我们可以使用三个任意整数(例如,0,1,2或88,128,168),三个字符串(“剪刀”,“布”,“石头”)来表示三个手势。主要缺点是我们需要检查程序中其他不可行的值(例如 3、"Rock"等)以确保正确性。

枚举是一种特殊类型,它为程序中的常量提供类型安全的实现。换句话说,我们可以声明一个类型的变量,它只接受预先定义的值。

阅读全文 »

Annotations是一种元数据,其作用在于提供程序本身以外的一些数据信息,也就是说Annotation他不会属于程序代码本身,不参与逻辑运算,故而不会对原程序代码的操作产生直接的影响。

  • 为编译器提供辅助信息 — Annotations可以为编译器提供而外信息,以便于检测错误,抑制警告等.
  • 编译源代码时进行而外操作 — 软件工具可以通过处理Annotation信息来生成原代码,xml文件等等.
  • 运行时处理 — 有一些annotation甚至可以在程序运行时被检测,使用.
阅读全文 »

反射常被用于在程序运行时检查或修改其执行行为。

为什么要使用反射

反射可以在下面几点帮助我们

  • 扩展功能
    • 扩展已有类的功能,例如有些字段不支持访问或修改,可以使用反射扩展。
  • 类浏览器和可视化开发
    • 类浏览器需要枚举类的成员,广泛应用于可视化开发环境
  • 调试和测试工具
    • 调试器需要能够检查类的私有成员,测试工具会使用反射调用API。

反射的弊端

反射很强大,但是不要滥用,如果可以避免使用,尽量避免。当使用反射时,下面的问题需要注意。

  • 性能开销
    • 反射会动态地处理对象,导致JVM的优化不能进行,因此,要避免在性能敏感地地方使用反射。
  • 安全限制
    • 反射可能会与安全管理相互冲突。
  • 内部资源暴露
    • 反射可以访问private方法,这可能会带来意料之外的副作用。
阅读全文 »

在JDK1.2之前,Java中的引用的定义很传统: 如果reference类型(栈中的引用类型)的数据中存储的数值代表另一块内存的起始地址,就称这块内存代表一个引用。在这种情况下,对象只有被引用和没有被引用两种状态。

我们希望有这样一类对象,当内存空间还足够时,保存在内存中;而如果内存空间在垃圾回收后还是紧张,则可以抛弃这些对象。

JDK1.2后,java对引用的概念进行了扩充,将引用分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phanton Reference)这四种,引用强度依次减弱。主要有两个目的:第一是可以让程序员通过代码的方式决定某些对象的生命周期;第二是有利于JVM进行垃圾回收。

阅读全文 »

前面介绍了Java对象在JVM中的内存布局。那么有什么方法可以方便的计算Java对象的内存占用?

本篇文章我们将介绍如何使用JOL分析Java对象内存。JOL是一个用来分析JVM中Object布局的小工具。包括Object在内存中的占用情况,实例对象的引用情况等等。JOL可以在代码中使用,也可以独立的以命令行中运行。

非特殊说明,本文中使用java默认为jdk8。

阅读全文 »

Java对象的内存布局:对象头(Header),实例数据(Instance Data)和对齐填充(Padding)。

1
2
3
4
5
6
7
+------------------+
| Header |
+------------------+
| Instance Data |
+------------------+
| Padding |
+------------------+

对象大小分为:

  • 自身的大小(Shadow heap size)
  • 所直接或间接引用的对象的大小(Retained heap size)。

本文讨论的是Java 对象的 Shadow heap size。Retained heap size可以通过遍历引用得到。本文Java 默认使用Oracle 1.8(即Hotspot虚拟机)。

阅读全文 »

java Instrumentation(插桩)指的是可以用独立于应用程序之外的代理(agent)程序来监测和协助运行在JVM上的应用程序。这种监测和协助包括但不限于获取JVM运行时状态,替换和修改类定义等。

从Java SE5开始使用JVM TI替代了JVM PI和JVM DI。提供一套代理机制,支持独立于JVM应用程序之外的程序以代理的方式连接和访问JVM。java.lang.instrument是在JVM TI的基础上提供的Java版本的实现。

Instrument 设计的目的是将字节码添加到方法中,收集工具使用的数据。出于这个目的,更改纯粹是附加的,因此这些工具不会修改应用程序状态或行为。此类良性工具的示例包括监控代理、分析器、覆盖分析器和事件记录器。

当然,由于这种检测机制提供了修改现有已编译 Java 类的字节码或添加字节码的能力,所以我们也可以用来动态修改运行的程序代码。

阅读全文 »

Java 对象启动顺序如下:

  1. java 对象初始化时,会先加载对应的类,随后加载其基类(如果存在基类);
  2. 先从基类执行static 初始化,后执行子类static 初始化。static初始化顺序:静态变量和静态初始化块的执行顺序取决于它们的代码位置顺序。
  3. 此时,类加载完毕,开始对象的初始化。先执行基类初始化,后执行衍生类初始化。
    • 对象初始化的顺序:
      • 属性得到初值:基本值类型为默认值;对象句柄为null;
        • 定义为final非静态基本数据类型的成员变量此时也会被初始化, 注意Integer等包装类不行;
        • 有且只有定义为final非静态的String成员变量,采用的“=”赋值初始化会被执行(非new)。
      • 属性定义时的初始化(例如int i =1)和代码块,的执行顺序取决于它们的代码位置顺序。
      • 构造函数;
阅读全文 »