0Pricing
Java Academy · レッスン

レイアウトと構造体

ネイティブメモリのレイアウトを記述します。

「レイアウトと構造体」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。

ネイティブメモリの記述

ネイティブデータが単一の値だけであることはほとんどありません。Cでは構造体、配列、共用体を使用します。FFMでは、これらの形状をメモリレイアウトでモデル化し、構造化されたネイティブメモリを安全に読み書きできます。

レイアウトの種類

レイアウト型は小さな階層を形成しています。

  • ValueLayout:プリミティブ(int、long、double、アドレス)
  • SequenceLayout:レイアウトの固定長配列
  • StructLayout:フィールドを順番に配置したもの
  • UnionLayout:フィールドが重なっているもの

ValueLayout

最も単純なレイアウトは、組み込みの値レイアウトです。サイズ、アラインメント、バイト順を保持しています。

import java.lang.foreign.ValueLayout;

public class Main {
    public static void main(String[] args) {
        System.out.println("int size: " + ValueLayout.JAVA_INT.byteSize());
        System.out.println("double size: " + ValueLayout.JAVA_DOUBLE.byteSize());
    }
}

構造体の定義

MemoryLayout.structLayout(...)を使ってCの構造体をモデル化し、各フィールドにwithNameで名前を付けます。Cのstruct Point { int x; int y; }を考えてみましょう。

import java.lang.foreign.*;

public class Main {
    public static void main(String[] args) {
        StructLayout point = MemoryLayout.structLayout(
            ValueLayout.JAVA_INT.withName("x"),
            ValueLayout.JAVA_INT.withName("y"));
        System.out.println("Point size: " + point.byteSize());
    }
}

フィールド用のVarHandle

名前付きフィールドを読み書きするには、パス式を使ってVarHandleを導出します。groupElement("x")は名前でフィールドを選択します。

import java.lang.foreign.*;
import java.lang.invoke.VarHandle;

public class Main {
    public static void main(String[] args) {
        StructLayout point = MemoryLayout.structLayout(
            ValueLayout.JAVA_INT.withName("x"),
            ValueLayout.JAVA_INT.withName("y"));
        VarHandle xHandle = point.varHandle(
            MemoryLayout.PathElement.groupElement("x"));
        System.out.println("Built VarHandle for x: " + xHandle);
    }
}

構造体の読み書き

レイアウトに合わせたサイズのセグメントを割り当て、var handleを使ってフィールドの設定と取得を行います。これはCの構造体に値を格納する処理に相当します。

import java.lang.foreign.*;
import java.lang.invoke.VarHandle;

public class Main {
    public static void main(String[] args) {
        StructLayout point = MemoryLayout.structLayout(
            ValueLayout.JAVA_INT.withName("x"),
            ValueLayout.JAVA_INT.withName("y"));
        VarHandle xh = point.varHandle(MemoryLayout.PathElement.groupElement("x"));
        VarHandle yh = point.varHandle(MemoryLayout.PathElement.groupElement("y"));
        try (Arena arena = Arena.ofConfined()) {
            MemorySegment p = arena.allocate(point);
            xh.set(p, 0L, 3);
            yh.set(p, 0L, 4);
            System.out.println("Point = (" + xh.get(p, 0L) + ", " + yh.get(p, 0L) + ")");
        }
    }
}

パディングとアラインメント

Cコンパイラーは、フィールドをアラインメントに合わせるためにパディングを挿入します。FFMはこれを自動的には追加しないため、Cのレイアウトと完全に一致させるにはMemoryLayout.paddingLayout(n)を含める必要があります。

アラインメントを間違えると、隙間の後にあるすべてのフィールドが破損します。

明示的なパディングの例

struct { char c; int n; }では、intを4バイト境界に合わせる必要があるため、charの後に3バイトのパディングが入ります。

import java.lang.foreign.*;

public class Main {
    public static void main(String[] args) {
        StructLayout s = MemoryLayout.structLayout(
            ValueLayout.JAVA_BYTE.withName("c"),
            MemoryLayout.paddingLayout(3),
            ValueLayout.JAVA_INT.withName("n"));
        System.out.println("Aligned struct size: " + s.byteSize());
    }
}

シーケンスレイアウト

SequenceLayoutは配列を記述します。MemoryLayout.sequenceLayout(count, element)を使うと、たとえば10個のdoubleの配列をモデル化できます。

import java.lang.foreign.*;

public class Main {
    public static void main(String[] args) {
        SequenceLayout arr = MemoryLayout.sequenceLayout(10, ValueLayout.JAVA_DOUBLE);
        System.out.println("Array bytes: " + arr.byteSize());
    }
}

入れ子になったレイアウト

レイアウトは組み合わせて使えます。構造体にシーケンスを含めたり、シーケンスに構造体を含めたりできます。sequenceElement()やgroupElement(name)のようなパス式で、入れ子になったデータの深い位置まで移動できます。

これにより、現実の複雑なCデータ構造にも対応できます。

オフセットとスライス

パスからフィールドのbyteOffsetを取得し、segment.asSlice(offset, size)を使ってサブセグメントを取得できます。これは、構造体の1つのフィールドをネイティブ関数に渡す場合に便利です。

確認問題

構造体でよくある落とし穴を思い出してください。

まとめ

ネイティブデータの記述方法を学びました。

  • レイアウトの種類:ValueLayout、SequenceLayout、StructLayout、UnionLayout
  • withNameでフィールドに名前を付け、VarHandleとパス要素でアクセスします
  • 正しいアラインメントのためにpaddingLayoutを明示的に追加します
  • 入れ子になったレイアウトを組み合わせ、asSliceでスライスします

これでForeign Function and Memory APIのコースは完了です。

よくある質問

「レイアウトと構造体」レッスンは無料ですか?

はい。「レイアウトと構造体」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。

「レイアウトと構造体」で何を学びますか?

ネイティブメモリのレイアウトを記述します。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Java Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「レイアウトと構造体」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このJava Academyレッスンでコードを書いて実行できますか?

はい。すべてのJava Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. JNI より FFM を使う理由
  2. MemorySegment と Arena
  3. ダウンコールハンドル
  4. レイアウトと構造体
← Java Academyに戻る