各种Java序列化性能比较
转载:http://www.jdon.com/concurrent/serialization.html
这里比较Java对象序列化 XML JSON Kryo POF等序列化性能比较。
很多人以为JDK的Java序列化肯定是将Java对象转换成二进制序列化最快的方式,JDK7出来以后,我们发现实际上每次新的JDK比旧版本快。
我们通常以为将Java对象序列化成二进制比序列化成XML或Json更快,其实是错误的,如果你关心性能,建议避免Java序列化。
Java序列化有很多的要求,最主要的一个是包含能够序列化任何东西(或至少任何实现Serializable接口)。这样才能进入其他JVM之中,这很重要,所以有时性能不是主要的要求,标准的格式才最重要。
我们经常看到CPU花费很多时间内进行Java序列化,下面我们研究一下,假设一定Order,虽然只有几个字节,但是序列化以后不是几十个字节,而是600多个字节:
Ordr代码:
public class Order implements Serializable {
private long id;
private String description;
private BigDecimal totalCost = BigDecimal.valueOf(0);
private List orderLines = new ArrayList();
private Customer customer; ... }
序列化输出:
----sr--model.Order----h#-----J--idL--customert--Lmodel/Customer;L--descriptiont--Ljava/lang/String;L--orderLinest--Ljava/util/List;L--totalCostt--Ljava/math/BigDecimal;xp--------ppsr--java.util.ArrayListx-----a----I--sizexp----w-----sr--model.OrderLine--&-1-S----I--lineNumberL--costq-~--L--descriptionq-~--L--ordert--Lmodel/Order;xp----sr--java.math.BigDecimalT--W--(O---I--scaleL--intValt--Ljava/math/BigInteger;xr--java.lang.Number-----------xp----sr--java.math.BigInteger-----;-----I--bitCountI--bitLengthI--firstNonzeroByteNumI--lowestSetBitI--signum[--magnitudet--[Bxq-~----------------------ur--[B------T----xp----xxpq-~--xq-~--
正如你可能已经注意到,Java序列化写入不仅是完整的类名,也包含整个类的定义,包含所有被引用的类。类定义可以是相当大的,也许构成了性能和效率的问题,当然这是编写一个单一的对象。如果您正在编写了大量相同的类的对象,这时类定义的开销通常不是一个大问题。另一件事情是,如果你的对象有一类的引用(如元数据对象),那么Java序列化将写入整个类的定义,不只是类的名称,因此,使用Java序列化写出元数据(meta-data)是非常昂贵的。
Externalizable
通过实现Externalizable接口,这是可能优化Java序列化的。实现此接口,避免写出整个类定义,只是类名被写入。它需要你实施readExternal和writeExternal方法方法的,所以需要做一些工作,但相比仅仅是实现Serializable更快,更高效。
Externalizable对小数目对象有效的多。但是对大量对象,或者重复对象,则效率低。
public class Order implements Externalizable {
private long id;
private String description;
private BigDecimal totalCost = BigDecimal.valueOf(0);
private List orderLines = new ArrayList();
private Customer customer; public Order() {
} public void readExternal(ObjectInput stream) throws IOException, ClassNotFoundException {
this.id = stream.readLong();
this.description = (String)stream.readObject();
this.totalCost = (BigDecimal)stream.readObject();
this.customer = (Customer)stream.readObject();
this.orderLines = (List)stream.readObject();
} public void writeExternal(ObjectOutput stream) throws IOException {
stream.writeLong(this.id);
stream.writeObject(this.description);
stream.writeObject(this.totalCost);
stream.writeObject(this.customer);
stream.writeObject(this.orderLines);
}
}
序列化输出:
----sr--model.Order---*3--^---xpw---------psr--java.math.BigDecimalT--W--(O---I--scaleL--intValt--Ljava/math/BigInteger;xr--java.lang.Number-----------xp----sr--java.math.BigInteger-----;-----I--bitCountI--bitLengthI--firstNonzeroByteNumI--lowestSetBitI--signum[--magnitudet--[Bxq-~----------------------ur--[B------T----xp----xxpsr--java.util.ArrayListx-----a----I--sizexp----w-----sr--model.OrderLine-!!|---S---xpw-----pq-~--q-~--xxx
EclipseLink MOXy - XML 和 JSON
序列化成XML或JSON可以允许其他语言访问,可以实现REST服务等。缺点是文本格式的效率比优化的二进制格式低一些,使用JAXB,你需要使用JAXB注释类,或提供一个XML配置文件。使用@XmlIDREF处理循环。
@XmlRootElement
public class Order {
@XmlID
@XmlAttribute
private long id;
@XmlAttribute
private String description;
@XmlAttribute
private BigDecimal totalCost = BigDecimal.valueOf(0);
private List orderLines = new ArrayList();
private Customer customer;
} public class OrderLine {
@XmlIDREF
private Order order;
@XmlAttribute
private int lineNumber;
@XmlAttribute
private String description;
@XmlAttribute
private BigDecimal cost = BigDecimal.valueOf(0);
}
XML输出:
<order id="0" totalCost="0">
<orderLines lineNumber="1" cost="0">
<order>0</order
></orderLines
></order>
JSOn输出:
{"order":{"id":0,"totalCost":0,"orderLines":[{"lineNumber":1,"cost":0,"order":0}]}}
Kryo
Kryo 是一种快速,高效的序列化的Java框架。 KRYO是新的BSD许可下一个开源项目提供。这是一个很小的项目,只有3名成员,它首先在2009年出品。
工作原理类似于Java序列化KRYO,尊重瞬态字段,但不要求一类是可序列化的。KRYO有一定的局限性,比如需要有一个默认的构造函数的类,在序列化将java.sql.Time java.sql.Date java.sql.Timestamp类会遇到一些问题。
order序列化结果:
------java-util-ArrayLis-----model-OrderLin----java-math-BigDecima---------model-Orde-----
Oracle Coherence POF
Oracle Coherence 产品提供其自己优化的二进制格式,称为POF (可移植对象格式) 。 Oracle Coherence的是一个内存中的数据网格解决方案(分布式缓存) 。是一个商业产品,并需要许可证。
POF提供了一个序列化框架,并可以独立使用。 POF要求类实现一个PortableObject接口和读/写方法。您还可以实现一个单独的序列化类,或使用最新版本的序列化的注解。 POF要求每个类都被提前分配一个固定ID,所以你需要通过某种方式确定这个ID 。 POF格式是二进制格式,非常紧凑,高效,快速的,但确实需要你付出一些工作。
POF的总字节数为一个单一的订单/订单行对象为32个字节, 1593字节100 OrderLines的。我不会放弃的结果, POF是一个商业许可产品的一部分,但是是非常快的。
public class Order implements PortableObject {
private long id;
private String description;
private BigDecimal totalCost = BigDecimal.valueOf(0);
private List orderLines = new ArrayList();
private Customer customer; public Order() {
} public void readExternal(PofReader in) throws IOException {
this.id = in.readLong(0);
this.description = in.readString(1);
this.totalCost = in.readBigDecimal(2);
this.customer = (Customer)in.readObject(3);
this.orderLines = (List)in.readCollection(4, new ArrayList());
} public void writeExternal(PofWriter out) throws IOException {
out.writeLong(0, this.id);
out.writeString(1, this.description);
out.writeBigDecimal(2, this.totalCost);
out.writeObject(3, this.customer);
out.writeCollection(4, this.orderLines);
}
}
序列化结果:
-----B--G---d-U------A--G-------
性能比较
一个订单包含一个Oderline
Serializer | Size (bytes) | Serialize (operations/second) | Deserialize (operations/second) | % Difference (from Java serialize) | % Difference (deserialize) |
---|---|---|---|---|---|
Java Serializable | 636 | 128,634 | 19,180 | 0% | 0% |
Java Externalizable | 435 | 160,549 | 26,678 | 24% | 39% |
EclipseLink MOXy XML | 101 | 348,056 | 47,334 | 170% | 146% |
Kryo | 90 | 359,368 | 346,984 | 179% | 1709% |
一个订单100个oderlines:
Serializer | Size (bytes) | Serialize (operations/second) | Deserialize (operations/second) | % Difference (from Java serialize) | % Difference (deserialize) |
---|---|---|---|---|---|
Java Serializable | 2,715 | 16,470 | 10,215 | 0% | 0% |
Java Externalizable | 2,811 | 16,206 | 11,483 | -1% | 12% |
EclipseLink MOXy XML | 6,628 | 7,304 | 2,731 | -55% | -73% |
Kryo | 1216 | 22,862 | 31,499 | 38% | 208% |
要获得象C那样的序列化性能,直接自己编写。
Serialization ByteBuffer Unsafe三者性能比较:
三者性能测试代码:
package com.ifenglian.test.safe; import sun.misc.Unsafe;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.util.Arrays; public final class TestSerialisationPerf {
public static final int REPETITIONS = 1 * 1000 * 1000; private static ObjectToBeSerialised ITEM = new ObjectToBeSerialised(1010L, true, 777, 99,
new double[] { 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0 },
new long[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); public static void main(final String[] arg) throws Exception {
for (final PerformanceTestCase testCase : testCases) {
for (int i = 0; i < 5; i++) {
testCase.performTest(); System.out.format("%d %s\twrite=%,dns read=%,dns total=%,dns\n", i, testCase.getName(),
testCase.getWriteTimeNanos(), testCase.getReadTimeNanos(),
testCase.getWriteTimeNanos() + testCase.getReadTimeNanos()); if (!ITEM.equals(testCase.getTestOutput())) {
throw new IllegalStateException("Objects do not match");
} System.gc();
Thread.sleep(3000);
}
}
} private static final PerformanceTestCase[] testCases = {
new PerformanceTestCase("Serialisation", REPETITIONS, ITEM) {
ByteArrayOutputStream baos = new ByteArrayOutputStream(); public void testWrite(ObjectToBeSerialised item) throws Exception {
for (int i = 0; i < REPETITIONS; i++) {
baos.reset(); ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(item);
oos.close();
}
} public ObjectToBeSerialised testRead() throws Exception {
ObjectToBeSerialised object = null;
for (int i = 0; i < REPETITIONS; i++) {
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
object = (ObjectToBeSerialised) ois.readObject();
} return object;
}
}, new PerformanceTestCase("ByteBuffer", REPETITIONS, ITEM) {
ByteBuffer byteBuffer = ByteBuffer.allocate(1024); public void testWrite(ObjectToBeSerialised item) throws Exception {
for (int i = 0; i < REPETITIONS; i++) {
byteBuffer.clear();
item.write(byteBuffer);
}
} public ObjectToBeSerialised testRead() throws Exception {
ObjectToBeSerialised object = null;
for (int i = 0; i < REPETITIONS; i++) {
byteBuffer.flip();
object = ObjectToBeSerialised.read(byteBuffer);
} return object;
}
}, new PerformanceTestCase("UnsafeMemory", REPETITIONS, ITEM) {
UnsafeMemory buffer = new UnsafeMemory(new byte[1024]); public void testWrite(ObjectToBeSerialised item) throws Exception {
for (int i = 0; i < REPETITIONS; i++) {
buffer.reset();
item.write(buffer);
}
} public ObjectToBeSerialised testRead() throws Exception {
ObjectToBeSerialised object = null;
for (int i = 0; i < REPETITIONS; i++) {
buffer.reset();
object = ObjectToBeSerialised.read(buffer);
} return object;
}
}, };
} abstract class PerformanceTestCase {
private final String name;
private final int repetitions;
private final ObjectToBeSerialised testInput;
private ObjectToBeSerialised testOutput;
private long writeTimeNanos;
private long readTimeNanos; public PerformanceTestCase(final String name, final int repetitions, final ObjectToBeSerialised testInput) {
this.name = name;
this.repetitions = repetitions;
this.testInput = testInput;
} public String getName() {
return name;
} public ObjectToBeSerialised getTestOutput() {
return testOutput;
} public long getWriteTimeNanos() {
return writeTimeNanos;
} public long getReadTimeNanos() {
return readTimeNanos;
} public void performTest() throws Exception {
final long startWriteNanos = System.nanoTime();
testWrite(testInput);
writeTimeNanos = (System.nanoTime() - startWriteNanos) / repetitions; final long startReadNanos = System.nanoTime();
testOutput = testRead();
readTimeNanos = (System.nanoTime() - startReadNanos) / repetitions;
} public abstract void testWrite(ObjectToBeSerialised item) throws Exception; public abstract ObjectToBeSerialised testRead() throws Exception;
} class ObjectToBeSerialised implements Serializable {
private static final long serialVersionUID = 10275539472837495L; private final long sourceId;
private final boolean special;
private final int orderCode;
private final int priority;
private final double[] prices;
private final long[] quantities; public ObjectToBeSerialised(final long sourceId, final boolean special, final int orderCode, final int priority,
final double[] prices, final long[] quantities) {
this.sourceId = sourceId;
this.special = special;
this.orderCode = orderCode;
this.priority = priority;
this.prices = prices;
this.quantities = quantities;
} public void write(final ByteBuffer byteBuffer) {
byteBuffer.putLong(sourceId);
byteBuffer.put((byte) (special ? 1 : 0));
byteBuffer.putInt(orderCode);
byteBuffer.putInt(priority); byteBuffer.putInt(prices.length);
for (final double price : prices) {
byteBuffer.putDouble(price);
} byteBuffer.putInt(quantities.length);
for (final long quantity : quantities) {
byteBuffer.putLong(quantity);
}
} public static ObjectToBeSerialised read(final ByteBuffer byteBuffer) {
final long sourceId = byteBuffer.getLong();
final boolean special = 0 != byteBuffer.get();
final int orderCode = byteBuffer.getInt();
final int priority = byteBuffer.getInt(); final int pricesSize = byteBuffer.getInt();
final double[] prices = new double[pricesSize];
for (int i = 0; i < pricesSize; i++) {
prices[i] = byteBuffer.getDouble();
} final int quantitiesSize = byteBuffer.getInt();
final long[] quantities = new long[quantitiesSize];
for (int i = 0; i < quantitiesSize; i++) {
quantities[i] = byteBuffer.getLong();
} return new ObjectToBeSerialised(sourceId, special, orderCode, priority, prices, quantities);
} public void write(final UnsafeMemory buffer) {
buffer.putLong(sourceId);
buffer.putBoolean(special);
buffer.putInt(orderCode);
buffer.putInt(priority);
buffer.putDoubleArray(prices);
buffer.putLongArray(quantities);
} public static ObjectToBeSerialised read(final UnsafeMemory buffer) {
final long sourceId = buffer.getLong();
final boolean special = buffer.getBoolean();
final int orderCode = buffer.getInt();
final int priority = buffer.getInt();
final double[] prices = buffer.getDoubleArray();
final long[] quantities = buffer.getLongArray(); return new ObjectToBeSerialised(sourceId, special, orderCode, priority, prices, quantities);
} @Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
} final ObjectToBeSerialised that = (ObjectToBeSerialised) o; if (orderCode != that.orderCode) {
return false;
}
if (priority != that.priority) {
return false;
}
if (sourceId != that.sourceId) {
return false;
}
if (special != that.special) {
return false;
}
if (!Arrays.equals(prices, that.prices)) {
return false;
}
if (!Arrays.equals(quantities, that.quantities)) {
return false;
} return true;
}
} class UnsafeMemory {
private static final Unsafe unsafe;
static {
try {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
unsafe = (Unsafe) field.get(null);
} catch (Exception e) {
throw new RuntimeException(e);
}
} private static final long byteArrayOffset = unsafe.arrayBaseOffset(byte[].class);
private static final long longArrayOffset = unsafe.arrayBaseOffset(long[].class);
private static final long doubleArrayOffset = unsafe.arrayBaseOffset(double[].class); private static final int SIZE_OF_BOOLEAN = 1;
private static final int SIZE_OF_INT = 4;
private static final int SIZE_OF_LONG = 8; private int pos = 0;
private final byte[] buffer; public UnsafeMemory(final byte[] buffer) {
if (null == buffer) {
throw new NullPointerException("buffer cannot be null");
} this.buffer = buffer;
} public void reset() {
this.pos = 0;
} public void putBoolean(final boolean value) {
unsafe.putBoolean(buffer, byteArrayOffset + pos, value);
pos += SIZE_OF_BOOLEAN;
} public boolean getBoolean() {
boolean value = unsafe.getBoolean(buffer, byteArrayOffset + pos);
pos += SIZE_OF_BOOLEAN; return value;
} public void putInt(final int value) {
unsafe.putInt(buffer, byteArrayOffset + pos, value);
pos += SIZE_OF_INT;
} public int getInt() {
int value = unsafe.getInt(buffer, byteArrayOffset + pos);
pos += SIZE_OF_INT; return value;
} public void putLong(final long value) {
unsafe.putLong(buffer, byteArrayOffset + pos, value);
pos += SIZE_OF_LONG;
} public long getLong() {
long value = unsafe.getLong(buffer, byteArrayOffset + pos);
pos += SIZE_OF_LONG; return value;
} public void putLongArray(final long[] values) {
putInt(values.length); long bytesToCopy = values.length << 3;
unsafe.copyMemory(values, longArrayOffset, buffer, byteArrayOffset + pos, bytesToCopy);
pos += bytesToCopy;
} public long[] getLongArray() {
int arraySize = getInt();
long[] values = new long[arraySize]; long bytesToCopy = values.length << 3;
unsafe.copyMemory(buffer, byteArrayOffset + pos, values, longArrayOffset, bytesToCopy);
pos += bytesToCopy; return values;
} public void putDoubleArray(final double[] values) {
putInt(values.length); long bytesToCopy = values.length << 3;
unsafe.copyMemory(values, doubleArrayOffset, buffer, byteArrayOffset + pos, bytesToCopy);
pos += bytesToCopy;
} public double[] getDoubleArray() {
int arraySize = getInt();
double[] values = new double[arraySize]; long bytesToCopy = values.length << 3;
unsafe.copyMemory(buffer, byteArrayOffset + pos, values, doubleArrayOffset, bytesToCopy);
pos += bytesToCopy; return values;
}
}
测试结果:
2.8GHz Nehalem - Java 1.7.0_04
==============================
0 Serialisation write=2,517ns read=11,570ns total=14,087ns
1 Serialisation write=2,198ns read=11,122ns total=13,320ns
2 Serialisation write=2,190ns read=11,011ns total=13,201ns
3 Serialisation write=2,221ns read=10,972ns total=13,193ns
4 Serialisation write=2,187ns read=10,817ns total=13,004ns
0 ByteBuffer write=264ns read=273ns total=537ns
1 ByteBuffer write=248ns read=243ns total=491ns
2 ByteBuffer write=262ns read=243ns total=505ns
3 ByteBuffer write=300ns read=240ns total=540ns
4 ByteBuffer write=247ns read=243ns total=490ns
0 UnsafeMemory write=99ns read=84ns total=183ns
1 UnsafeMemory write=53ns read=82ns total=135ns
2 UnsafeMemory write=63ns read=66ns total=129ns
3 UnsafeMemory write=46ns read=63ns total=109ns
4 UnsafeMemory write=48ns read=58ns total=106ns
2.4GHz Sandy Bridge - Java 1.7.0_04
===================================
0 Serialisation write=1,940ns read=9,006ns total=10,946ns
1 Serialisation write=1,674ns read=8,567ns total=10,241ns
2 Serialisation write=1,666ns read=8,680ns total=10,346ns
3 Serialisation write=1,666ns read=8,623ns total=10,289ns
4 Serialisation write=1,715ns read=8,586ns total=10,301ns
0 ByteBuffer write=199ns read=198ns total=397ns
1 ByteBuffer write=176ns read=178ns total=354ns
2 ByteBuffer write=174ns read=174ns total=348ns
3 ByteBuffer write=172ns read=183ns total=355ns
4 ByteBuffer write=174ns read=180ns total=354ns
0 UnsafeMemory write=38ns read=75ns total=113ns
1 UnsafeMemory write=26ns read=52ns total=78ns
2 UnsafeMemory write=26ns read=51ns total=77ns
3 UnsafeMemory write=25ns read=51ns total=76ns
4 UnsafeMemory write=27ns read=50ns total=77ns
很显然允许自己内存操作的 Unsafe性能是最快的。
各种Java序列化性能比较的更多相关文章
- java序列化框架(protobuf、thrift、kryo、fst、fastjson、Jackson、gson、hessian)性能对比
我们为什么要序列化 举个栗子:下雨天我们要打伞,但是之后我们要把伞折叠起来,方便我们存放.那么运用到我们java中道理是一样的,我们要将数据分解成字节流,以便存储在文件中或在网络上传输,这叫序列 ...
- [java]序列化框架性能对比(kryo、hessian、java、protostuff)
序列化框架性能对比(kryo.hessian.java.protostuff) 简介: 优点 缺点 Kryo 速度快,序列化后体积小 跨语言支持较复杂 Hessian 默认支持跨语言 较慢 Pro ...
- java原生序列化和Kryo序列化性能比较
简介 最近几年,各种新的高效序列化方式层出不穷,不断刷新序列化性能的上限,最典型的包括: 专门针对Java语言的:Kryo,FST等等 跨语言的:Protostuff,ProtoBuf,Thrift, ...
- 透过byte数组简单分析Java序列化、Kryo、ProtoBuf序列化
序列化在高性能网络编程.分布式系统开发中是举足轻重的之前有用过Java序列化.ProtocolBuffer等,在这篇文章这里中简单分析序列化后的byte数组观察各种序列化的差异与性能,这里主要分析Ja ...
- 编解码-java序列化
大多数Java程序员接触到的第一种序列化或者编解码技术就是Java的默认序列化,只需要序列化的POJO对象实现java.io.Serializable接口,根据实际情况生成序列ID,这个类就能够通过j ...
- 用 JMH 检测 Lambdas 序列化性能
本文将介绍如何进行 Java Lambdas 序列化性能检测.Lambdas 的重要性以及 Lambdas 在分布式系统中的应用. Lambdas 表达式是 Java 8 中万众期待的新特性,其若干用 ...
- (中级篇 NettyNIO编解码开发)第七章-java序列化
相信大多数Java程序员接触到的第一种序列化或者编解码技术就是.Java的默认序列化,只需要序列化的POJO对象实现java.io.Serializable接口,根据实际情况生成序列ID,这个类就能够 ...
- Java序列化框架性能比較
博客: http://colobu.com jvm-serializers提供了一个非常好的比較各种Java序列化的的測试套件. 它罗列了各种序列化框架. 能够自己主动生成測试报告. 我在AWS c3 ...
- 输入和输出--java序列化机制
对象的序列化 什么是Java对象的序列化? 对象序列化的目标是将对象保存到磁盘上,或允许在网络中直接传输对象.对象序列化机制允许把内存中的Java对象转换成与平台无关的二进制流,从而保存或者传输.其他 ...
随机推荐
- Vs2015智能提示英文?
Vs2015智能提示英文? 装了vs2015代码的智能提示全部变成英文了 找到这个目录 C:\Program Files (x86)\Reference Assemblies\Microsoft\ ...
- [手机取证] Jonathan Zdziarski公开的苹果iOS后门及POC视频
Jonathan Zdziarski 近日在其推特上公布了此“后门”的研究及POC视频,并表示全球媒体“夸大”了此事,自己“从未表示过认为此后门与NSA的监控行为有关”. 视频 http://pan. ...
- 转ASP.NET1.1请求队列限制
在教务web的选课的维护中,经常面临asp.net1.1报错,在客户端跳转到用户自定义页面,在服务器端可以看到如下错误信息: “/”应用程序中的服务器错误. 服务器太忙 说明: 执行当前 Web 请求 ...
- 【原】redis插件安装
wget -c https://github.com/nicolasff/phpredis/archive/2.2.4.tar.gz -O phpredis-2.2.4.tar.gz tar xzf ...
- Ice分布式程序设计—IceBox(Hello World Application)
忙了三天,总算浏览完此书.藉此记下 Ice 的 IceBox 服务框架. 在此用 IceBox 框架写 Hello World 程序,即以载体来体现其特性. 第一步:编写 Slice 文件,映射生成 ...
- ZAM 3D 制作简单的3D字幕 流程(一)
本文原地址-> http://www.cnblogs.com/yk250/p/5663048.html 效果参考图:请查阅 http://www.cnblogs.com/yk250/p/5662 ...
- Javascript中判断变量是 array还是object(是数组还是对象)
段文字是从github上截取由本人翻译过来的. 原文地址:https://github.com/nathansmith/javascript-quiz/blob/master/ANSWERS.md 怎 ...
- 移动端WEB页面
百度前端技术学院第一阶段任务十一,关于移动端WEB页面布局,参考资料如下(都是一些网页链接): MDN:手机网页开发 MDN:在移动浏览器中使用viewport元标签控制布局 移动前端开发和 Web ...
- bashrc
# ~/.bashrc: executed by bash(1) for non-login shells.# see /usr/share/doc/bash/examples/startup-fil ...
- 好用的內存鏡像工具Belkasoft RAM Capture
来自俄罗斯的取证大厂Belkasoft,旗下的主力产品Belkasoft Evidence Center有不错的评价,除了BEC之外,咱们Yuri老兄也是佛心来着的,提供了一个免费内存镜像工具RamC ...