前言

Warden是现在被广泛使用在CloudFoundry中的容器技术,在CloudFoundry V3版本中,Warden被重新命名为Garden,我们暂且还是以V2版本做分析。众所周知,Docker是另一个易于使用并且很有效率的容器,所以现在CloudFoundry也支持Docker容器技术。接下来,我们就谈一谈Warden与Docker的相似和不同。

相似

都是依赖宿主操作系统内核的轻量级Container

目前,PaaS越来越多的与Container联系在一起,Container的高资源利用率恰恰是PaaS需要的。Warden和Docker都是Container的一种,相对于虚拟机来说,Container占用的资源比较少,都是依赖于宿主操作系统内核的轻量级容器。依赖于宿主机的操作系统,这样做会带来哪些好处呢?
一个虚拟机占用的资源比一个Container占用的资源多十几倍。设想一下,在一台物理机上启动100台虚拟机会是非常困难的,因为有硬件资源的限制,会出现CPU、硬盘或者内存资源用尽的情况。当相比之下,在一台物理机上启动100个Container是很简单的,因为作为Container,他会依赖于宿主机操作系统内核,仅仅在资源和服务等方面进行了隔离,启动快速,占用资源很少。现在很多大型的互联网公司都在大量地使用Container技术,Facebook一台虚机都没有,因为这些互联网公司要求充分利用计算资源。虚机起码还要再跑一个Guest系统,太耗资源。同时,在这些大公司内部并没有很多操作系统,集群只有一种操作系统,甚至连版本号都是固定的。因此,不需要虚机的异构操作系统特性。另外,在操作系统上还有Runtime Library,Container不需要重复加载,极大的节省内存占用。

阅读更多

前言

当前大多数PaaS云平台只支持特定的开发框架,开发者只能部署平台支持的框架类型的应用程序。CloudFoundry云平台支持各种框架的灵活选择,这些框架包括Spring for Java,.NET,Ruby on Rails,Node.js,Grails,Scala on Lift以及更多合作伙伴提供的框架(如Python,PHP等),大大提高了平台的灵活性。

CloudFoundry云平台将应用和应用依赖的服务相分开,通过在部署时将应用和应用依赖的服务相绑定的机制使应用和应用服务相对对立,增加了在PaaS平台上部署应用的灵活性。这些应用服务包括PostgreSQL,MySQL,SQL Server,MongoDB,Redis以及更多来自第三方和开源社区的应用服务。

CloudFoundry云平台架构

CloudFoundry是由相对独立的多个模块构成的分布式系统,每个模块单独存在和运行,各模块之间通过消息机制进行通信。CloudFoundry各模块本身是基于Ruby语言开发的,每个部分可以认为拿来即可运行,不存在编译等过程。CloudFoundry简化了应用程序的开发、交付和运行,使开发者在云环境中部署、运行和扩展应用程序的能力得以大幅提升,并支持种类最为广泛的公共云和私有云、基于行业标准的高效开发框架和应用基础架构服务。

阅读更多

Class文件是一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑的排列在Class文件之中,中间没有添加任何分隔符。
根据Java虚拟机的规定,Class文件格式采用一种类似于C语言结构体的伪结构来存储,这种伪结构中只有两种数据类型:五符号数和表。

  • 无符号数属于基本的数据类型:u1、u2、u4、u8来分辨代表1个字节、2个字节、4个字节、8个字节的无符号数。
  • 表是由多个无符号数或其他表作为数据项构成的符合数据类型。

    class文件的内部结构

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    ClassFile { 
    u4 magic;
    u2 minor_version;
    u2 major_version;
    u2 constant_pool_count;
    cp_info constant_pool[constant_pool_count-1];
    u2 access_flags;
    u2 this_class;
    u2 super_class;
    u2 interfaces_count;
    u2 interfaces[interfaces_count];
    u2 fields_count;
    field_info fields[fields_count];
    u2 methods_count;
    method_info methods[methods_count];
    u2 attributes_count;
    attribute_info attributes[attributes_count];
    }

阅读更多

判断对象现存状态

堆中几乎存放着Java世界中所有的实例对象,垃圾收集器在对堆进行回收之前,首先要判断哪些对象是存活状态,哪些对象是死亡状态。

引用计数法

在JDK1.2之前,使用的是引用计数器算法,即当这个类被加载到内存以后,就会产生方法区,堆栈、程序计数器等一系列信息,当创建对象的时候,为这个对象在堆栈空间中分配对象,同时会产生一个引用计数器,每当有一个地方引用它时,计数器值就加1,当引用失效时计时器值就减1,在任何时刻计数器都为0的对象就是不可能再被使用的。
在Java语言中并没有选用这种算法去管理内存,原因是它很难解决两个对象之间的相互引用问题。
比如说:对象objA和objB中都有属性instance,赋值令 objA.instance = objB 以及 objB.instance = objA,除此之外,两个对象之间没有其他的引用。那么此时情况就是两个对象已经不能再被访问,但是他们之间因为相互引用对方,导致它们的引用计数都不为0,因此也就不会被回收。

阅读更多

Java内存模型

运行时内存区域

Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域,每个区域有各自的用途,有着不同的创建和销毁时间。

程序计数器

程序计数器是一块较小的内存空间,可以看做是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型中,字节码解释器工作就是通过改变这个计数器的值来选取下一个需要执行的字节码的指令,分支、循环、跳转、异常处理、线程恢复等基础的功能都需要依赖这个计数器来完成。

Java虚拟机栈

Java虚拟机栈是描述Java方法栈执行的内存模型,每一个方法被执行的时候都会同时创建一个栈帧用于存储局部变量表、操作栈、动态链接等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在Java虚拟机栈的从入栈到出栈的过程。

本地方法栈

与Java虚拟机栈不同的是,本地方法栈是为Java虚拟机执行native方法服务的。

Java堆

Java堆是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建,目的就是为了存放对象实例。同时Java堆也是垃圾收集器管理的主要区域。

方法区

方法区是也是一个线程共享的内存区域,用于存放已被虚拟机加载的类的信息、常量、静态变量、即时编译器编译后的代码等。

运行时常量池

运行时常量池是方法区的一部分,主要用于存放编译期间生成的各种字面量和符号引用。

在Java中对象访问是如何进行的?

在Java语言中,对象访问无处不在,任何对象的访问都会涉及到Java栈、Java堆和方法区这三个最重要的内存区域。

1
Object obj = new Object();

分析上面一行代码,Object obj会翻译到Java栈的本地变量表中,最为一个reference类型数据出现,
new Object()这部分语义会反应到Java堆中,形成一块存储了Object类型的结构化内存,
在方法区中会存放此段对象数据类型的地址信息,包括类型,父类等等的地址信息。

句柄访问方式

句柄访问方式
java堆中将划分出一块内存来作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象实例数据和类型数据各自的具体地址信息。

直接指针访问方式

直接指针访问方式
reference变量中直接存储的就是对象的地址,而java堆对象一部分存储了对象实例数据,另外一部分存储了对象类型数据。

这两种访问对象的方式各有优势,使用句柄访问方式最大好处就是reference中存储的是稳定的句柄地址,在对象移动时只需要改变句柄中的实例数据指针,而reference不需要改变。使用指针访问方式最大好处就是速度快,它节省了一次指针定位的时间开销,就虚拟机而言,它使用的是第二种方式(直接指针访问)。

内存溢出异常(OutOfMemoryError)

  • Java堆溢出
    Java堆用于存放对象实例,如果我们不断地创建对象,并且在GC不会回收的情况下,就会造成Java堆溢出。
  • 虚拟机栈和本地方法栈溢出
  • 运行时常良池溢出
  • 方法区溢出
  • 本机直接溢出

体验泛型

在Jdk1.5之后,定义集合时需要你明确集合中要装哪种类型的数据,无法加入指定类型之外的数据。

1
2
3
4
ArrayList<Integer> arr1 = new ArrayList<Integer>(); 
arr1.add(1);
arr1.add("aa"); //有的泛型之后,此行代码在编译的时候会报错
int x = arr1.get(0); //有了泛型之后,取数据的时候不需要进行强制类型转换

什么是泛型

泛型是提供给javac编译器使用的,可以限定集合中的输入类型,让编译器挡住源程序的非法输入。
需要注意的是:编译器编译带类型参数的集合时会出掉“类型”信息,使程序运行的效率不受影响。对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样。由于编译器生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往泛型集合中加入其它类型的数据。
举例,用泛型得到集合,再调用其add方法

1
2
3
ArrayList<Integer> arr1 = new ArrayList<Integer>(); 
arr1.getClass().getMethod("add", Object.class).invoke(arr1, "aaa");
System.out.println(arr1.get(0)); //可以在控制台输出aaa

阅读更多

概述

  • 多对多关联映射指的是两个对象之间是多对多的关系,比如teacher-student。
  • 由于在操作和性能方面都不太理想,所以多对多的映射使用较少,实际使用中最好转换成一对多的对象模型,Hibernate会为我们创建中间关联表,转换成两个一对多关联关系。

    E-R图

    阅读更多

概述

一对一关联映射指的是两个对象之间是一对一的关系,比如person—id_card。
一对一关联映射可以分为两种:

  1. 主键关联:两个对象具有相同的主键值,以表明他们之间的一一对应的关系,数据库表不会有额外的字段去维护他们之间的关系,仅通过表的主键来关联。
  2. 外键关联:本来是用于一对多的配置,但是如果加上唯一的限制之后,也可以用来表示一对一关联关系。

    主键关联

    举例来说明主键关联,数据库中有一张person表,用来存放人员信息,包括姓名和年龄,还有一张id_card表,存放身份证信息。

    E-R图

    阅读更多