日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java:从Java 8开始受益于内联类属性

發布時間:2023/12/3 java 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java:从Java 8开始受益于内联类属性 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

希望在幾年內,Java將具有“內聯類”功能,該功能可以解決Java當前狀態下的許多挑戰。 閱讀本文并學習如何立即使用Java 8或更高版本,并且仍將受益于即將出現的內聯對象數組的一些優點,例如; 沒有間接指針,消除了對象標頭開銷,并改善了數據局部性。

在本文中,我們將學習如何編寫一個名為
InlineArray支持將來的許多內聯類功能。 我們還將看一下Speedment HyperStream,這是一個使用類似操作方法的現有Java工具。

背景

自1995年以來,當從完全合理的角度出發時,Java中的Objects數組由一個數組組成,該數組又包含對其他對象的大量引用,這些引用最終分散在堆中。

現在,這是在Java中將具有兩個初始Point對象的數組布置在堆上的方式:

Array +======+ |Header| +------+ Point 0 |ref 0 |---> +======+ +------+ |Header| Point +------+ |Header| Point 1 |ref 1 |---- +------+ ---> +======+ +------+ |x | |Header| | null | +------+ +------+ | +------+ +------+ +------+ |y | |x | | null | +------+ +------+ | +------+ +------+ +------+ |y | |... | +------+ +------+

但是,隨著時間的流逝,典型的CPU的執行流水線已經發生了巨大的變化,并且計算性能得到了驚人的提高。 另一方面,光速保持恒定,因此,不幸的是,從主存儲器加載數據的等待時間保持在相同的數量級內。 計算和檢索之間的平衡已偏向于計算。

這些天來訪問主內存已成為我們要避免的事情,就像我們希望避免過去從旋轉磁盤中加載數據一樣。

顯然,當前的Object數組布局具有幾個缺點,例如:

  • 雙內存訪問(由于數組中的間接引用指針)
  • 數據局部性降低(因為數組對象放置在堆中的不同位置)
  • 增加的內存占用量(因為數組中引用的所有對象都是對象,因此擁有附加的Class和同步信息)。

內聯類

在Java社區內,現在正在付出很大的努力來引入“內聯類”(以前稱為“值類”)。 這項工作的最新狀態(截至2019年7月)由Brian Goetz i提出。
在此視頻中,標題為“ Project Valhalla Update(2019版)”。 沒有人知道何時在正式的Java版本中提供此功能。 我個人的猜測是2021年以后的某個時候。

一旦此功能可用,以下是如何排列嵌入式Point對象的數組:

Array +======+ |Header| +------+ |x | +------+ |y | +------+ |x | +------+ |y | +------+ |... | +------+

可以看出,該方案消耗更少的內存(沒有Point頭),提高了局部性(數據按順序放置在內存中),并且可以直接訪問數據而無需遵循間接引用指針。 另一方面,我們丟失了對象身份的概念,本文稍后將對此進行討論。

模擬一些內聯類屬性

在下面,我們將對內聯類的某些屬性進行仿真。 應當注意,下面的所有示例現在都可以在標準Java 8和更高版本上運行。

假設我們有一個interface Point帶有X和Y吸氣劑,如下所述:

public interface Point { int x(); int y(); } y(); }

然后,我們可以輕松地創建一個不變的實現
Point界面如下圖所示:

public final class VanillaPoint implements Point { private final int x, y; public VanillaPoint( int x, int y) { this .x = x; this .y = y; } @Override public int x() { return x; } x; } @Override public int y() { return y; } y; } // toString(), equals() and hashCode() not shown for brevity }

此外,假設我們愿意放棄數組中Point對象的Object / identity屬性。 這意味著,除其他外,我們無法同步或執行身份操作(例如==和System::identityHashCode )

這里的想法是創建一個內存區域,我們可以直接在字節級別使用該內存區域,并在那里將對象展平。 可以將這個內存區域封裝在一個名為InlineArray<T>的通用類中,如下所示:

public final class InlineArray<T> { private final ByteBuffer memoryRegion; private final int elementSize; private final int length; private final BiConsumer<ByteBuffer, T> deconstructor; private final Function<ByteBuffer,T> constructor; private final BitSet presentFlags; public InlineArray( int elementSize, int length, BiConsumer<ByteBuffer, T> deconstructor, Function<ByteBuffer,T> constructor ) { this .elementSize = elementSize; this .length = length; this .deconstructor = requireNonNull(deconstructor); this .constructor = requireNonNull(constructor); this .memoryRegion = ByteBuffer.allocateDirect(elementSize * length); this .presentFlags = new BitSet(length); } public void put( int index, T value) { assertIndexBounds(index); if (value == null ) { presentFlags.clear(index); } else { position(index); deconstructor.accept(memoryRegion, value); presentFlags.set(index); } } public T get( int index) { assertIndexBounds(index); if (!presentFlags.get(index)) { return null ; } position(index); return constructor.apply(memoryRegion); } public int length() { return length; } private void assertIndexBounds( int index) { if (index < 0 || index >= length) { throw new IndexOutOfBoundsException( "Index [0, " + length + "), was:" + index); } } private void position( int index) { memoryRegion.position(index * elementSize); } }

請注意,此類可以處理任何類型的元素( T類型),但前提是它具有最大的元素大小,但可以將其解構(序列化)為字節。 如果所有元素的元素大小都與Point相同,則該類效率最高(即始終為Integer.BYTES * 2 = 8字節)。 還要注意,該類不是線程安全的,但是可以添加該類以增加內存屏障為代價,并且根據解決方案使用ByteBuffer單獨視圖。

現在,假設我們要分配一萬個點的數組。 有了新的InlineArray類,我們可以這樣進行:

public class Main { public static void main(String[] args) { InlineArray<Point> pointArray = new InlineArray<>( Integer.BYTES * 2 , // The max element size 10_000, (bb, p) -> {bb.putInt(px()); bb.putInt(py());}, bb -> new VanillaPoint(bb.getInt(), bb.getInt()) ); Point p0 = new VanillaPoint( 0 , 0 ); Point p1 = new VanillaPoint( 1 , 1 ); pointArray.put( 0 , p0); // Store p0 at index 0 pointArray.put( 1 , p1); // Store p1 at index 1 System.out.println(pointArray.get( 0 )); // Should produce (0, 0) System.out.println(pointArray.get( 1 )); // Should produce (1, 1) System.out.println(pointArray.get( 2 )); // Should produce null } }

如預期的那樣,代碼在運行時將產生以下輸出:

VanillaPoint{x= 0 , y= 0 } VanillaPoint{x= 1 , y= 1 } null

請注意,我們如何向InlineArray提供元素解構函數和元素構造函數,以告知其應如何解構和構造
Point對象指向線性存儲器或從線性存儲器Point對象。

仿真屬性

上面的模擬可能不會獲得與真正的內聯類相同的性能提升,但是在內存分配和位置方面的節省將是大致相同的。 上面的模擬是分配堆外內存,因此您的垃圾回收時間將不受InlineArray放置的元素數據的InlineArray 。 ByteBuffer中的元素的布局就像建議的內聯類數組一樣:

Array +======+ |Header| +------+ |x | +------+ |y | +------+ |x | +------+ |y | +------+ |... | +------+

由于我們使用ByteBuffer被索引與對象
int ,后備存儲區域被限制為2 ^ 31個字節。 例如,這意味著我們只能將2 ^(31-3)= 2 ^ 28≈2.68億
在我們用盡地址空間之前,數組中的Point元素(因為每個點占用2 ^ 3 = 8個字節)。 實際的實現可以通過使用多個ByteBuffer,Unsafe或Chronicle Bytes之類的庫來克服此限制。

懶惰的實體

給定InlineArray類,從中提供元素非常容易
InlineArray是惰性的,在某種意義上,當從數組中檢索元素時,它們不必急于反序列化所有字段。 這是可以做到的:

首先,我們創建Point接口的另一種實現,該實現從后備ByteBuffer本身而不是本地字段中獲取其數據:

public final class LazyPoint implements Point { private final ByteBuffer byteBuffer; private final int position; public LazyPoint(ByteBuffer byteBuffer) { this .byteBuffer = byteBuffer; this .position = byteBuffer.position(); } @Override public int x() { return byteBuffer.getInt(position); } @Override public int y() { return byteBuffer.getInt(position + Integer.BYTES); } // toString(), equals() and hashCode() not shown for brevity }

然后,我們只需要替換粘貼到
InlineArray是這樣的:

InlineArray pointArray = new InlineArray<>( Integer.BYTES * 2 , 10_000, (bb, p) -> {bb.putInt(px()); bb.putInt(py());}, LazyPoint:: new // Use this deserializer instead );

如果使用與上述相同的主要方法,將產生以下輸出:

LazyPoint{x= 0 , y= 0 } LazyPoint{x= 1 , y= 1 } null

涼。 這對于具有數十個甚至數百個字段的實體特別有用,因為對于其中的問題,只能訪問字段的有限子集。

這種方法的缺點是,如果在我們的應用程序中僅保留一個LazyPoint引用,則它將阻止整個后備ByteBuffer垃圾回收。 因此,像這樣的任何惰性實體都最好用作短期對象。

使用大量數據

如果我們想使用非常大的數據集合(例如,以TB為單位),可能來自數據庫或文件,并將其有效地存儲在JVM內存中,然后能夠與這些集合一起使用以提高計算性能,該怎么辦? 我們可以使用這種技術嗎?

Speedment HyperStream是一種利用類似技術能夠將數據庫數據作為標準Java Streams提供的產品,并且已經有一段時間了。 HyperStream可以按上述方式布置數據,并且可以在單個JVM中存儲TB級的數據,而對Garbage Collection的影響很小或沒有影響,因為數據是非堆存儲的。 它可以使用就地反序列化直接從后備存儲區域中獲得單個字段,從而避免了不必要的實體完全反序列化。 它的標準Java流是確定性的超低延遲,在某些情況下可以在100 ns內構造和使用流。

這是在電影之間進行分頁時如何在應用程序中使用HyperStream(實現標準Java Stream)的示例。 的
Manager films變量由Speedment自動提供:

private Stream<Film> getPage( int page, Comparator<Film> comparator) { return films.stream() .sorted(comparator) .skip(page * PAGE_SIZE) .limit(PAGE_SIZE) }

即使可能有數萬億的影片,該方法通常也將在不到一微秒的時間內完成,因為Stream直接連接到RAM并使用內存索引。

在此處閱讀有關Speedment HyperStream性能的更多信息。

通過在此處下載Speedment HyperStream 來評估自己的數據庫應用程序中的性能。

資源資源

瓦爾哈拉計劃https://openjdk.java.net/projects/valhalla/

Speedment HyperStream https://www.speedment.com/hyperstream/ Speedment初始化程序https://www.speedment.com/initializer/

翻譯自: https://www.javacodegeeks.com/2019/08/java-benefit-inline-class-properties-starting.html

創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的Java:从Java 8开始受益于内联类属性的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。