Results 56 issues of Sayi

本文从虚拟机加载和执行层面探讨下类或者接口(下文统一使用类)、对象的生命周期。 ## 命令行编译和执行代码 在初学Java的时候很多人都学过在没有IDE的情况下编译(javac)和执行(java)代码,后来你会彻底爱上IDE,它可以自动编译和管理依赖,你只需要运行代码就行了,我们这里先来回顾下如何通过命令行编译和执行代码,项目目录结构如下: ``` +-| com |----+- deepoove |-----------+- java8 |---------------+- def |-------------------\- Apple.java |---------------\- ClassLoaderTest.java ``` 我们写个非常简单的类ClassLoaderTest,引用了Gson类和目录下面的Apple类: ```java package com.deepoove.java8; import com.deepoove.java8.def.Apple; import com.google.gson.Gson; public class ClassLoaderTest {...

JVM

应用的故障有时候会体现在JVM层面,大多数JVM的问题又是一个时间问题,不是很快能复现或者到某个时间点才发生,分析和解决这类问题就显得尤为重要,本文就从内存和线程两方面阐述问题解决思路以及介绍一些JDK提供的工具,关于JVM致命错误(System Crashes)及更多类型的故障解决,可以参见: > [Java Platform, Standard Edition Troubleshooting Guide](https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/index.html) ## 内存泄漏Memory Leaks和内存溢出OutOfMemoryError OutOfMemoryError的种类有很多,参考[Understand the OutOfMemoryError Exception](https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/memleaks002.html)和[OutOfMemoryError](https://tier1app.files.wordpress.com/2014/12/outofmemoryerror2.pdf),通过上篇文章我们已经知道虚拟机运行时哪些数据区域会定义内存溢出,接下来我们就来看看如何解决这些区域的内存问题。 ### **第一条:分析GC日志** 当内存空间不够时,会触发GC,Minor GC是年轻代频繁的GC,当发生Full GC我们就需要重点关注了,通过GC日志我们可以清晰的看到每次GC的效果,那么 **我们如何查看GC日志呢**? 我们需要通过参数打开GC日志: | Options | 说明 | | ---...

JVM

虚拟机和Java语言一开始的设计是独立的,这也产生了很多面向虚拟机的语言,如Groovy等,它们最终生成格式标准的字节码文件被虚拟机装载和解析。本文对虚拟机的一些核心内容进行介绍,包括运行时数据区域和垃圾回收。 > [The Java® Virtual Machine Specification](https://docs.oracle.com/javase/specs/jvms/se8/html/index.html) ## 运行时数据区域(Run-Time Data Areas) Java虚拟机定义了在程序执行期间使用的各种运行时数据区域,其中一些数据区域是在Java虚拟机启动时创建的,仅在Java虚拟机退出时销毁。还有一些数据是每个线程拥有,这些线程数据区域是在线程退出时创建和销毁线程时创建的。我们先来看看一张图([Java Garbage Collection Basics](https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html)): ![image](https://user-images.githubusercontent.com/1394854/49358537-0036e300-f70e-11e8-8a30-1c6124b7c97e.png) 从图中可以看到,总共分为5个区域,**方法区和堆是所有线程共享的,其余三个区域是线程私有的**。 ### 1.程序计数器Program counter register 每个线程都拥有自己的pc Register,可以简单的理解为当前线程的执行位置。在任意时刻,一条JVM线程只会执行一个方法的代码,该方法称为该线程的当前方法(Current Method), **如果该方法是Java方法,那计数器保存JVM正在执行的字节码指令的地址,如果该方法是Native,那PC寄存器的值为空(Undefined)**。 ### 2.栈Java Stacks 每个线程都私有一个线程栈,存储线程使用的局部变量等信息。当前栈的大小可以通过命令`java -XX:+PrintFlagsInitial...

JVM

Java Collections Framework基本介绍完了,当我们遇到性能或者有需需要定义自己的集合时,比如希望数据不是保存在内存中而是从数据库迭代,可以扩展JDK提供的明确设计的抽象实现AbstractXXX接口。 Guava提供了一些特别的集合实现和工具类,本文旨在入门Guava Collections,分为三个部分: 1. 更多的集合类型 2. 不可变集合 3. 工具 详细文档请参阅官方文档:https://github.com/google/guava/wiki。 ## 更多的集合类型 ### Multiset Multiset是一个元素可以重复的集合,Multiset可以被看做一个无需关心顺序ArrayList,或者一个元素和元素个数的Map。如果没有这个类,我们通常的实现会是这样的: ```java new ArrayList() 或者 new HashMap ``` Multiset **继承了JDK Collection接口**,并且提供了一些额外的方法。 |方法|作用| | --...

Collections框架

Collections工具类方便集合的操作。 ## 不可修改包装Unmodifiable Wrappers ```java public static Collection unmodifiableCollection(Collection c); public static Set unmodifiableSet(Set s); public static List unmodifiableList(List list); public static Map unmodifiableMap(Map m); public static SortedSet unmodifiableSortedSet(SortedSet s);...

Collections框架

本文接着上篇继续探讨更多的Map实现和Set实现。 ## LinkedHashMap LinkedHashMap是一个有序的HashMap,它 **继承了HashMap类,并且通过内部维护一个链表来保证迭代时有序**。 ```java static class Entry extends HashMap.Node { Entry before, after; Entry(int hash, K key, V value, Node next) { super(hash, key, value, next); } }...

Collections框架

Map是一个键值对映射的集合,不允许重复的键,允许null的键或者值,关于Map接口的方法,请参考《Java Collections Framework(一)概览》。 前面提到过,Map和Collection是独立的两个接口,Java平台提供了三种通用实现: HashMap,LinkedHashMap和TreeMap,从名称中不难发现,Java没有采用可变数组为基础的实现,比如ArrayMap,或者单纯以链表为基础的实现,比如LinkedMap,这里有个重要的单词:Hash,即散列。 本文将详细讨论这些Map的实现原理。 ## 基础知识:哈希表 在讨论Map之前,我们先来熟悉一下数据结构中提及到的哈希表。 哈希表是一种以常量平均时间进行插入和查找的技术,哈希表实现通常是一个数组,将不同的关键字映射到数组某个下标的位置,所以需要一个哈希函数来计算数组中的位置,如index = f(key)。 理想情况下,哈希函数尝试将不同的Key关联到一个唯一的数组下标index,但是由于数组大小是一定的,这就有可能导致不同的Key映射到同一个index,这就是哈希碰撞。 **哈希函数** 哈希表的性能取决于选择一个好的哈希函数。如果映射的Key是一个整数,那么我们可以 f(key) = key % size 来映射到index,但是如果这些整数具有一些特征,比如size如果是10,key的值也正好都是10的倍数,那么显然这个哈希函数就是糟糕的。 如果映射的key不是一个整数,而是字符串或者一个对象呢?Java通过hashcode()方法来计算这些对象的int类型散列码,然后再处理这个散列码,使其成为数组的下标。我们来看看HashMap中计算哈希函数的源码: ```java static final int hash(Object key) { int...

Collections框架

Deque是一个双向队列,它继承了Queue接口,除了作为双向队列使用外,既可以作为 **FIFO队列和栈LIFO使用,即同时实现了堆栈和队列**。 通用实现有LinkedList和ArrayDeque,前面已经讨论过LinkedList作为队列来使用,下面看一段如何作为栈来使用的代码: ```java LinkedList linkedListDeque = new LinkedList(); linkedListDeque.push("hello"); linkedListDeque.push("i"); linkedListDeque.push("am"); linkedListDeque.push("stack"); org.junit.Assert.assertEquals("stack", linkedListDeque.pop()); ``` 本文将会对ArrayDeque详细阐述。 ## Deque接口 我们先来看看Deque接口。 ```java public interface Deque extends Queue { // *** deque...

Collections框架

队列是一个典型的FIFO先进先出结构,从一端放入元素,从另一端取出元素。队列在并发环境中比较有用,但是Java也提供了线程不安全的实现:LinkedList和PriorityQueue,ArrayDeque也实现了Queue的所有方法,它是基于可变数组实现的队列(关于ArrayDeque将在Deque章节中详细介绍)。 LinkedList的实现原理在前文中已经介绍过,下面给出LinkedList作为队列使用的一段示例代码,本文将对PriorityQueue作详细介绍。 ```java LinkedList linkedListDeque = new LinkedList(); linkedListDeque.offer("hello"); linkedListDeque.offer("i"); linkedListDeque.offer("am"); linkedListDeque.offer("queue"); org.junit.Assert.assertEquals("hello", linkedListDeque.peek()); org.junit.Assert.assertEquals("hello", linkedListDeque.poll()); ``` ## Queue接口 我们先来看看Queue接口。 ```java public interface Queue extends Collection { boolean add(E e);...

Collections框架

LinkedList实现了List接口,它使用链表方式实现了有序的序列结构。LinkedList又是一个有趣的存在,它还实现了Deque接口,而Deque接口是实现了Queue接口,所以LinkedList同时也实现了一个双向队列、队列或者栈结构。 本文将阐述LinkedList的实现原理,重点放在List接口方法实现上,关于Deque的方法使用,将在后续关于双向队列的章节中提及。 ## LinkedList实现原理 ![image](https://user-images.githubusercontent.com/1394854/44837221-9c62d100-ac6b-11e8-92c1-af8be548f248.png) LinkedList是基于双向链表实现的,内部维护了首尾两个节点:Node first和Node last,其中Node的结构体如下: ```java private static class Node { E item; Node next; Node prev; Node(Node prev, E element, Node next) { this.item = element;...

Collections框架