Increase search result relevance (#8)

* Increase accuracy of the position bits.
* Increase their width to 56.
* Use a rolling position scheme for bits 16-56 to increase the average accuracy.
* Result ranking overhaul
* Optimized queries
* BM25 in the index service's ranking
* Make gui less jank
* Javadocs for ranking parameters.
This commit is contained in:
Viktor 2023-04-07 20:18:08 +02:00 committed by GitHub
parent f1c6525a50
commit a278fc6296
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 2111 additions and 623 deletions

View File

@ -1,6 +1,7 @@
package nu.marginalia.index.client.model.query;
import lombok.*;
import nu.marginalia.index.client.model.results.ResultRankingParameters;
import nu.marginalia.index.query.limit.QueryLimits;
import nu.marginalia.index.query.limit.QueryStrategy;
import nu.marginalia.index.query.limit.SpecificationLimit;
@ -27,4 +28,5 @@ public class SearchSpecification {
public final QueryStrategy queryStrategy;
public final ResultRankingParameters rankingParams;
}

View File

@ -0,0 +1,11 @@
package nu.marginalia.index.client.model.results;
/** Tuning parameters for BM25.
*
* @param k determines the size of the impact of a single term
* @param b determines the magnitude of the length normalization
*
* @see nu.marginalia.ranking.factors.Bm25Factor
*/
public record Bm25Parameters(double k, double b) {
}

View File

@ -0,0 +1,38 @@
package nu.marginalia.index.client.model.results;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import lombok.ToString;
import java.util.Map;
@ToString
public class ResultRankingContext {
private final int docCount;
public final ResultRankingParameters params;
private final Object2IntOpenHashMap<String> fullCounts = new Object2IntOpenHashMap<>(10, 0.5f);
private final Object2IntOpenHashMap<String> priorityCounts = new Object2IntOpenHashMap<>(10, 0.5f);
public ResultRankingContext(int docCount,
ResultRankingParameters params,
Map<String, Integer> fullCounts,
Map<String, Integer> prioCounts
) {
this.docCount = docCount;
this.params = params;
this.fullCounts.putAll(fullCounts);
this.priorityCounts.putAll(prioCounts);
}
public int termFreqDocCount() {
return docCount;
}
public int frequency(String keyword) {
return fullCounts.getOrDefault(keyword, 1);
}
public int priorityFrequency(String keyword) {
return priorityCounts.getOrDefault(keyword, 1);
}
}

View File

@ -0,0 +1,51 @@
package nu.marginalia.index.client.model.results;
import lombok.AllArgsConstructor;
import lombok.Builder;
@Builder @AllArgsConstructor
public class ResultRankingParameters {
/** Tuning for BM25 when applied to full document matches */
public final Bm25Parameters fullParams;
/** Tuning for BM25 when applied to priority matches, terms with relevance signal indicators */
public final Bm25Parameters prioParams;
/** Documents below this length are penalized */
public int shortDocumentThreshold;
public double shortDocumentPenalty;
/** Scaling factor associated with domain rank (unscaled rank value is 0-255; high is good) */
public double domainRankBonus;
/** Scaling factor associated with document quality (unscaled rank value is 0-15; high is bad) */
public double qualityPenalty;
/** Average sentence length values below this threshold are penalized, range [0-4), 2 or 3 is probably what you want */
public int shortSentenceThreshold;
/** Magnitude of penalty for documents with low average sentence length */
public double shortSentencePenalty;
public double bm25FullWeight;
public double bm25PrioWeight;
public double tcfWeight;
public static ResultRankingParameters sensibleDefaults() {
return builder()
.fullParams(new Bm25Parameters(1.2, 0.5))
.prioParams(new Bm25Parameters(1.5, 0))
.shortDocumentThreshold(2000)
.shortDocumentPenalty(2.)
.domainRankBonus(1/25.)
.qualityPenalty(1/15.)
.shortSentenceThreshold(2)
.shortSentencePenalty(5)
.bm25FullWeight(1.)
.bm25PrioWeight(1.)
.tcfWeight(2.)
.build();
}
}

View File

@ -31,16 +31,13 @@ public final class SearchResultKeywordScore {
}
public int positionCount() {
return Integer.bitCount(positions());
return Long.bitCount(positions());
}
public int tfIdf() {
return (int) WordMetadata.decodeTfidf(encodedWordMetadata);
}
public int subquery() {
return subquery;
}
public int positions() {
public long positions() {
return WordMetadata.decodePositions(encodedWordMetadata);
}

View File

@ -1,67 +1,42 @@
package nu.marginalia.index.client.model.results;
import nu.marginalia.model.idx.DocumentMetadata;
import org.jetbrains.annotations.NotNull;
import static java.lang.Boolean.compare;
import static java.lang.Integer.compare;
import static java.lang.Double.compare;
public record SearchResultPreliminaryScore(boolean hasSingleTermMatch,
boolean hasPriorityTerm,
int minNumberOfFlagsSet,
int minNumberOfPositions,
int overlappingPositions,
boolean anyAllSynthetic,
int avgSentenceLength,
int topology
)
public record SearchResultPreliminaryScore(
boolean anyAllSynthetic,
int minNumberOfFlagsSet,
int minPositionsSet,
boolean hasPriorityTerm,
double searchRankingScore)
implements Comparable<SearchResultPreliminaryScore>
{
public SearchResultPreliminaryScore(long documentMetadata,
boolean hasSingleTermMatch,
boolean hasPriorityTerm,
int minNumberOfFlagsSet,
int minNumberOfPositions,
int overlappingPositions,
boolean anyAllSynthetic
)
{
this(hasSingleTermMatch, hasPriorityTerm, minNumberOfFlagsSet, minNumberOfPositions, overlappingPositions, anyAllSynthetic,
DocumentMetadata.decodeAvgSentenceLength(documentMetadata),
DocumentMetadata.decodeTopology(documentMetadata)
);
}
final static int PREFER_HIGH = 1;
final static int PREFER_LOW = -1;
@Override
public int compareTo(@NotNull SearchResultPreliminaryScore other) {
int diff;
diff = -compare(avgSentenceLength >= 2, other.avgSentenceLength >= 2);
diff = PREFER_HIGH * compare(hasPriorityTerm, other.hasPriorityTerm);
if (diff != 0) return diff;
diff = compare(hasSingleTermMatch, other.hasSingleTermMatch);
if (diff != 0) return diff;
diff = compare(minNumberOfFlagsSet, other.minNumberOfFlagsSet);
if (diff != 0) return diff;
diff = compare(hasPriorityTerm, other.hasPriorityTerm);
if (diff != 0) return diff;
diff = compare(overlappingPositions, other.overlappingPositions);
if (diff != 0) return diff;
diff = compare(minNumberOfPositions, other.minNumberOfPositions);
if (diff != 0) return diff;
return -compare(topology, other.topology);
return PREFER_LOW * compare(searchRankingScore, other.searchRankingScore);
}
public boolean isEmpty() {
return minNumberOfFlagsSet == 0
&& minNumberOfPositions == 0
&& overlappingPositions == 0
&& !anyAllSynthetic;
if (minNumberOfFlagsSet > 0)
return false;
if (anyAllSynthetic)
return false;
if (minPositionsSet > 0)
return false;
return true;
}
}

View File

@ -1,25 +0,0 @@
package nu.marginalia.index.client.model.results;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import lombok.ToString;
import java.util.Map;
@ToString
public class SearchResultRankingContext {
private final int docCount;
private final Object2IntOpenHashMap<String> termCounts = new Object2IntOpenHashMap<>(10, 0.5f);
public SearchResultRankingContext(int docCount, Map<String, Integer> termCounts) {
this.docCount = docCount;
this.termCounts.putAll(termCounts);
}
public int termFreqDocCount() {
return docCount;
}
public int frequency(String keyword) {
return termCounts.getOrDefault(keyword, 1);
}
}

View File

@ -9,7 +9,7 @@ import java.util.List;
@AllArgsConstructor @Getter @ToString
public class SearchResultSet {
public List<SearchResultItem> results;
public SearchResultRankingContext rankingContext;
public ResultRankingContext rankingContext;
public int size() {
return results.size();
}

View File

@ -9,7 +9,6 @@ import java.util.Set;
public class ApiSearchResultQueryDetails {
String keyword;
int tfIdf;
int count;
Set<String> flagsUnstableAPI;

View File

@ -6,22 +6,15 @@ import nu.marginalia.bbpc.BrailleBlockPunchCards;
import java.util.EnumSet;
import java.util.Set;
import static java.lang.Math.max;
import static java.lang.Math.min;
public record WordMetadata(int tfIdf,
int positions,
public record WordMetadata(long positions,
byte flags) {
// Bottom 16 bits are used for flags
public static final long FLAGS_MASK = 0xFFFFL;
public static final long FLAGS_MASK = 0xFFL;
public static final long TF_IDF_MASK = 0xFFFFL;
public static final int TF_IDF_SHIFT = 16;
public static final int POSITIONS_SHIFT = 32;
public static final long POSITIONS_MASK = 0xFFFF_FFFFL;
public static final int POSITIONS_SHIFT = 8;
public static final long POSITIONS_MASK = 0xFF_FFFF_FFFF_FFFFL;
@ -31,17 +24,15 @@ public record WordMetadata(int tfIdf,
public WordMetadata(long value) {
this(
(int)((value >>> TF_IDF_SHIFT) & TF_IDF_MASK),
(int)((value >>> POSITIONS_SHIFT) & POSITIONS_MASK),
((value >>> POSITIONS_SHIFT) & POSITIONS_MASK),
(byte) (value & FLAGS_MASK)
);
}
public WordMetadata(int tfIdf,
int positions,
public WordMetadata(long positions,
Set<WordFlags> flags)
{
this(tfIdf, positions, encodeFlags(flags));
this(positions, encodeFlags(flags));
}
private static byte encodeFlags(Set<WordFlags> flags) {
@ -56,12 +47,8 @@ public record WordMetadata(int tfIdf,
public static boolean hasAnyFlags(long encoded, long metadataBitMask) {
return (encoded & metadataBitMask) != 0;
}
public static int decodePositions(long meta) {
return (int) (meta >>> POSITIONS_SHIFT);
}
public static double decodeTfidf(long meta) {
return (meta >>> TF_IDF_SHIFT) & TF_IDF_MASK;
public static long decodePositions(long meta) {
return (meta >>> POSITIONS_SHIFT) & POSITIONS_MASK;
}
public boolean hasFlag(WordFlags flag) {
@ -69,12 +56,7 @@ public record WordMetadata(int tfIdf,
}
public String toString() {
StringBuilder sb = new StringBuilder(getClass().getSimpleName());
sb.append('[')
.append("tfidf=").append(tfIdf).append(", ")
.append("positions=[").append(BrailleBlockPunchCards.printBits(positions, 32)).append(']');
sb.append(", flags=").append(flagSet()).append(']');
return sb.toString();
return "[positions=%s; %s]".formatted(BrailleBlockPunchCards.printBits(positions, 56), flagSet());
}
/* Encoded in a 64 bit long
@ -83,14 +65,13 @@ public record WordMetadata(int tfIdf,
long ret = 0;
ret |= Byte.toUnsignedLong(flags);
ret |= min(TF_IDF_MASK, max(0, tfIdf)) << TF_IDF_SHIFT;
ret |= ((long)(positions)) << POSITIONS_SHIFT;
ret |= (positions & POSITIONS_MASK) << POSITIONS_SHIFT;
return ret;
}
public boolean isEmpty() {
return positions == 0 && flags == 0 && tfIdf == 0;
return positions == 0 && flags == 0;
}
public static long emptyValue() {
@ -102,7 +83,4 @@ public record WordMetadata(int tfIdf,
return WordFlags.decode(flags);
}
public int positionCount() {
return Integer.bitCount(positions);
}
}

View File

@ -12,29 +12,13 @@ class WordMetadataTest {
@Test
public void codecTest() {
verifyCodec("Vanilla case", new WordMetadata(32, 0x7f0f0000, EnumSet.allOf(WordFlags.class)));
verifyCodec("Position high", new WordMetadata(32, 0xff0f0000, EnumSet.allOf(WordFlags.class)));
verifyCodec("No flags", new WordMetadata(32, 0xff0f0000, EnumSet.noneOf(WordFlags.class)));
System.out.println(new WordMetadata(32, 0x7f0f0005, EnumSet.allOf(WordFlags.class)));
System.out.println(new WordMetadata(32, 0xff0f0013, EnumSet.noneOf(WordFlags.class)));
}
@Test
public void testClampTfIdfLow() {
var original = new WordMetadata(0x8000FFFF, 0, EnumSet.noneOf(WordFlags.class));
var encoded = new WordMetadata(original.encode());
assertEquals(original.positions(), encoded.positions());
assertEquals(0, encoded.tfIdf());
}
@Test
public void testClampTfIdfHigh() {
var original = new WordMetadata(0x7000FFFF, 0, EnumSet.noneOf(WordFlags.class));
var encoded = new WordMetadata(original.encode());
assertEquals(original.positions(), encoded.positions());
assertEquals(65535, encoded.tfIdf());
verifyCodec("Vanilla case", new WordMetadata(0x7f0f0000L, EnumSet.allOf(WordFlags.class)));
verifyCodec("Position 32bit", new WordMetadata(0xff0f0000L, EnumSet.allOf(WordFlags.class)));
verifyCodec("Position all", new WordMetadata(0xffff_ff0f_0000L, EnumSet.allOf(WordFlags.class)));
verifyCodec("No flags", new WordMetadata( 0xff0f0000L, EnumSet.noneOf(WordFlags.class)));
System.out.println(new WordMetadata(0x7f0f0005L, EnumSet.allOf(WordFlags.class)));
System.out.println(new WordMetadata(0xff0f0013L, EnumSet.noneOf(WordFlags.class)));
System.out.println(new WordMetadata(0xf0f000ff0f0013L, EnumSet.allOf(WordFlags.class)));
}
public void verifyCodec(String message, WordMetadata data) {

View File

@ -56,9 +56,9 @@ class KeywordMetadata {
if (urlKeywords.containsDomain(stemmed))
flags.add(WordFlags.UrlDomain);
int positions = bitmask.get(stemmed);
long positions = bitmask.get(stemmed);
return new WordMetadata(tfidf, positions, flags).encode();
return new WordMetadata(positions, flags).encode();
}
}

View File

@ -1,13 +1,16 @@
package nu.marginalia.keyword.extractors;
import com.google.inject.Inject;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
import nu.marginalia.keyword.KeywordExtractor;
import nu.marginalia.language.model.DocumentLanguageData;
/** Generates a position bitmask for each word in a document */
public class KeywordPositionBitmask {
private final Object2IntOpenHashMap<String> positionMask = new Object2IntOpenHashMap<>(10_000, 0.7f);
private final Object2LongOpenHashMap<String> positionMask = new Object2LongOpenHashMap<>(10_000, 0.7f);
private final static int positionWidth = 56;
private final static long positionBitmask = (1L << positionWidth) - 1;
private static final int unmodulatedPortion = 16;
@Inject
public KeywordPositionBitmask(KeywordExtractor keywordExtractor, DocumentLanguageData dld) {
@ -29,7 +32,7 @@ public class KeywordPositionBitmask {
LinePosition linePos = new LinePosition();
for (var sent : dld.sentences) {
int posBit = (int)((1L << linePos.pos()) & 0xFFFF_FFFFL);
long posBit = (1L << linePos.pos()) & positionBitmask;
for (var word : sent) {
positionMask.merge(word.stemmed(), posBit, this::bitwiseOr);
@ -43,58 +46,41 @@ public class KeywordPositionBitmask {
}
}
public int get(String stemmed) {
public long get(String stemmed) {
return positionMask.getOrDefault(stemmed, 0);
}
private int bitwiseOr(int a, int b) {
private long bitwiseOr(long a, long b) {
return a | b;
}
private static class LinePosition {
private int lineLengthCtr = 0;
private int line = 0;
private int bitMaskPos = 1;
public int pos() {
return bitMaskPos;
}
public void next(int sentenceLength) {
if (bitMaskPos < 4) bitMaskPos++;
else if (bitMaskPos < 8) {
if (advanceLine(sentenceLength)>= 2) {
bitMaskPos++;
line = 0;
}
if (bitMaskPos < unmodulatedPortion) {
return bitMaskPos;
}
else if (bitMaskPos < 24) {
if (advanceLine(sentenceLength) >= 4) {
bitMaskPos++;
line = 0;
}
}
else if (bitMaskPos < 64) {
if (advanceLine(sentenceLength) > 8) {
bitMaskPos++;
line = 0;
}
else {
return unmodulatedPortion + ((bitMaskPos - unmodulatedPortion) % (positionWidth - unmodulatedPortion));
}
}
private int advanceLine(int sentenceLength) {
public void next(int sentenceLength)
{
if (sentenceLength > 10) {
lineLengthCtr = 0;
return ++line;
++bitMaskPos;
}
lineLengthCtr += sentenceLength;
if (lineLengthCtr > 15) {
lineLengthCtr = 0;
return ++line;
++bitMaskPos;
}
return line;
}
}
}

View File

@ -67,13 +67,6 @@ public class WordsTfIdfCounts implements WordReps {
for (var sent : dld.sentences) {
var keywords = keywordExtractor.getKeywordsFromSentence(sent);
for (var span : keywords) {
if (span.size() == 1
&& WordPatterns.isStopWord(sent.words[span.start]))
{
continue;
}
counts.addTo(spanToStemmed(sent, span), 1);
}
}

View File

@ -8,7 +8,7 @@ import nu.marginalia.model.idx.WordMetadata;
import java.util.*;
@ToString @Getter
@Getter
public class DocumentKeywordsBuilder {
public final Object2LongLinkedOpenHashMap<String> words;
@ -37,8 +37,8 @@ public class DocumentKeywordsBuilder {
return new DocumentKeywords(wordArray, meta);
}
public DocumentKeywordsBuilder(int cacpacity) {
words = new Object2LongLinkedOpenHashMap<>(cacpacity);
public DocumentKeywordsBuilder(int capacity) {
words = new Object2LongLinkedOpenHashMap<>(capacity);
}
public void add(String word, long meta) {
@ -92,9 +92,7 @@ public class DocumentKeywordsBuilder {
@Override
public String toString() {
StringBuilder sb = new StringBuilder("[ ");
words.forEach((word, meta) -> {
sb.append(word).append("->").append(new WordMetadata(meta)).append(' ');
});
words.forEach((word, meta) -> sb.append(word).append("->").append(new WordMetadata(meta)).append(' '));
return sb.append(']').toString();
}

View File

@ -55,5 +55,21 @@ class DocumentKeywordExtractorTest {
System.out.println(new WordMetadata(566820053975498886L));
// -
System.out.println(new WordMetadata(1198298103937L));
System.out.println(new WordMetadata(1103808168065L));
}
@Test
public void testSpam() throws IOException, URISyntaxException {
var resource = Objects.requireNonNull(ClassLoader.getSystemResourceAsStream("test-data/spam.html"),
"Could not load word frequency table");
String html = new String(resource.readAllBytes(), Charset.defaultCharset());
var doc = Jsoup.parse(html);
doc.filter(new DomPruningFilter(0.5));
DocumentKeywordExtractor extractor = new DocumentKeywordExtractor(new TermFrequencyDict(WmsaHome.getLanguageModels()));
SentenceExtractor se = new SentenceExtractor(WmsaHome.getLanguageModels());
var keywords = extractor.extractKeywords(se.extractSentences(doc), new EdgeUrl("https://math.byu.edu/wiki/index.php/All_You_Need_To_Know_About_Earning_Money_Online"));
System.out.println(keywords.getMetaForWord("knitting"));
}
}

File diff suppressed because one or more lines are too long

View File

@ -63,4 +63,16 @@ public class ReverseIndexPriorityReader {
return new ReverseIndexRetainFilter(createReaderNew(offset), "priority", wordId);
}
public int numDocuments(int wordId) {
if (wordId < 0)
return 0;
long offset = words.get(wordId);
if (offset < 0)
return 0;
return createReaderNew(offset).numEntries();
}
}

View File

@ -29,4 +29,22 @@ public class Token {
return s.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;");
}
public void visit(TokenVisitor visitor) {
switch (type) {
case QUOT_TERM: visitor.onQuotTerm(this); break;
case EXCLUDE_TERM: visitor.onExcludeTerm(this); break;
case PRIORTY_TERM: visitor.onPriorityTerm(this); break;
case ADVICE_TERM: visitor.onAdviceTerm(this); break;
case NEAR_TERM: visitor.onNearTerm(this); break;
case LITERAL_TERM: visitor.onLiteralTerm(this); break;
case YEAR_TERM: visitor.onYearTerm(this); break;
case RANK_TERM: visitor.onRankTerm(this); break;
case SIZE_TERM: visitor.onSizeTerm(this); break;
case QS_TERM: visitor.onQsTerm(this); break;
case QUALITY_TERM: visitor.onQualityTerm(this); break;
}
}
}

View File

@ -0,0 +1,16 @@
package nu.marginalia.query_parser.token;
public interface TokenVisitor {
void onLiteralTerm(Token token);
void onQuotTerm(Token token);
void onExcludeTerm(Token token);
void onPriorityTerm(Token token);
void onAdviceTerm(Token token);
void onNearTerm(Token token);
void onYearTerm(Token token);
void onSizeTerm(Token token);
void onRankTerm(Token token);
void onQualityTerm(Token token);
void onQsTerm(Token token);
}

View File

@ -1,7 +1,9 @@
# Result Ranking
Contains various heuristics for deciding which search results are important
with regard to a query.
with regard to a query. In broad strokes [BM-25](https://nlp.stanford.edu/IR-book/html/htmledition/okapi-bm25-a-non-binary-model-1.html)
is used, with a number of additional bonuses and penalties to rank the appropriate search
results higher.
## Central Classes

View File

@ -1,24 +1,25 @@
package nu.marginalia.ranking;
import nu.marginalia.index.client.model.results.SearchResultKeywordScore;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
public record ResultKeywordSet(SearchResultKeywordScore[] keywords) implements Iterable<SearchResultKeywordScore> {
@NotNull
@Override
public Iterator<SearchResultKeywordScore> iterator() {
return Arrays.stream(keywords).iterator();
}
public record ResultKeywordSet(List<SearchResultKeywordScore> keywords) {
public int length() {
return keywords.length;
return keywords.size();
}
public boolean isEmpty() { return length() == 0; }
public boolean hasNgram() {
for (var word : keywords) {
if (word.keyword.contains("_")) {
return true;
}
}
return false;
}
@Override
public String toString() {
return "%s[%s]".formatted(getClass().getSimpleName(), Arrays.toString(keywords));
return "%s[%s]".formatted(getClass().getSimpleName(), keywords);
}
}

View File

@ -1,6 +1,6 @@
package nu.marginalia.ranking;
import nu.marginalia.index.client.model.results.SearchResultRankingContext;
import nu.marginalia.index.client.model.results.ResultRankingContext;
import nu.marginalia.index.client.model.results.SearchResultKeywordScore;
import nu.marginalia.model.idx.DocumentMetadata;
import nu.marginalia.ranking.factors.*;
@ -9,103 +9,152 @@ import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import static java.lang.Math.min;
@Singleton
public class ResultValuator {
private final TermFlagsFactor termFlagsFactor;
final static double scalingFactor = 250.;
private final Bm25Factor bm25Factor;
private final TermCoherenceFactor termCoherenceFactor;
private final PriorityTermFactor priorityTermFactor;
private final PriorityTermBonus priorityTermBonus;
private final ThreadLocal<ValuatorListPool<SearchResultKeywordScore>> listPool =
ThreadLocal.withInitial(ValuatorListPool::new);
@Inject
public ResultValuator(TermFlagsFactor termFlagsFactor,
Bm25Factor bm25Factor,
public ResultValuator(Bm25Factor bm25Factor,
TermCoherenceFactor termCoherenceFactor,
PriorityTermFactor priorityTermFactor) {
PriorityTermBonus priorityTermBonus) {
this.termFlagsFactor = termFlagsFactor;
this.bm25Factor = bm25Factor;
this.termCoherenceFactor = termCoherenceFactor;
this.priorityTermFactor = priorityTermFactor;
this.priorityTermBonus = priorityTermBonus;
}
public double calculateSearchResultValue(List<SearchResultKeywordScore> scores,
int length,
int titleLength,
SearchResultRankingContext ctx)
ResultRankingContext ctx)
{
var threadListPool = listPool.get();
int sets = numberOfSets(scores);
double bestBm25Factor = 10;
double allTermsFactor = 1.;
double bestScore = 10;
final double priorityTermBonus = priorityTermFactor.calculate(scores);
long documentMetadata = documentMetadata(scores);
var rankingParams = ctx.params;
int rank = DocumentMetadata.decodeRank(documentMetadata);
int asl = DocumentMetadata.decodeAvgSentenceLength(documentMetadata);
int quality = DocumentMetadata.decodeQuality(documentMetadata);
int topology = DocumentMetadata.decodeTopology(documentMetadata);
double averageSentenceLengthPenalty = (asl >= rankingParams.shortSentenceThreshold ? 0 : -rankingParams.shortSentencePenalty);
double qualityPenalty = -quality * rankingParams.qualityPenalty;
double rankingBonus = (255. - rank) * rankingParams.domainRankBonus;
double topologyBonus = Math.log(1 + topology);
double documentLengthPenalty = length > rankingParams.shortDocumentThreshold ? 0 : -rankingParams.shortDocumentPenalty;
double overallPart = averageSentenceLengthPenalty
+ documentLengthPenalty
+ qualityPenalty
+ rankingBonus
+ topologyBonus
+ priorityTermBonus.calculate(scores);
for (int set = 0; set <= sets; set++) {
ResultKeywordSet keywordSet = createKeywordSet(scores, set);
ResultKeywordSet keywordSet = createKeywordSet(threadListPool, scores, set);
final double bm25 = bm25Factor.calculate(keywordSet, length, ctx);
if (keywordSet.isEmpty() || keywordSet.hasNgram())
continue;
bestBm25Factor = min(bestBm25Factor, bm25);
allTermsFactor *= getAllTermsFactorForSet(keywordSet, titleLength);
final double tcf = rankingParams.tcfWeight * termCoherenceFactor.calculate(keywordSet);
final double bm25 = rankingParams.bm25FullWeight * bm25Factor.calculateBm25(rankingParams.fullParams, keywordSet, length, ctx);
final double bm25p = rankingParams.bm25PrioWeight * bm25Factor.calculateBm25Prio(rankingParams.prioParams, keywordSet, ctx);
double score = normalize(bm25 + bm25p + tcf + overallPart, keywordSet.length());
bestScore = min(bestScore, score);
}
var meta = docMeta(scores);
double lenFactor = Math.max(1.0, 2500. / (1.0 + length));
return bestBm25Factor * (0.4 + 0.6 * allTermsFactor) * priorityTermBonus * lenFactor;
return bestScore;
}
private Optional<DocumentMetadata> docMeta(List<SearchResultKeywordScore> rawScores) {
return rawScores
.stream().map(SearchResultKeywordScore::encodedDocMetadata)
.map(DocumentMetadata::new).findFirst();
}
public double getAllTermsFactorForSet(ResultKeywordSet set, int titleLength) {
double totalFactor = 1.;
totalFactor *= termFlagsFactor.calculate(set, titleLength);
if (set.length() > 1) {
totalFactor *= 1.0 - 0.5 * termCoherenceFactor.calculate(set);
private long documentMetadata(List<SearchResultKeywordScore> rawScores) {
for (var score : rawScores) {
return score.encodedDocMetadata();
}
assert (Double.isFinite(totalFactor));
return totalFactor;
return 0;
}
private ResultKeywordSet createKeywordSet(List<SearchResultKeywordScore> rawScores,
private ResultKeywordSet createKeywordSet(ValuatorListPool<SearchResultKeywordScore> listPool,
List<SearchResultKeywordScore> rawScores,
int thisSet)
{
ArrayList<SearchResultKeywordScore> scoresList = new ArrayList<>(rawScores.size());
List<SearchResultKeywordScore> scoresList = listPool.get(thisSet);
scoresList.clear();
for (var score : rawScores) {
if (score.subquery != thisSet)
continue;
if (score.keyword.contains(":"))
// Don't consider synthetic keywords for ranking, these are keywords that don't
// have counts. E.g. "tld:edu"
if (score.isKeywordSpecial())
continue;
scoresList.add(score);
}
return new ResultKeywordSet(scoresList.toArray(SearchResultKeywordScore[]::new));
return new ResultKeywordSet(scoresList);
}
private int numberOfSets(List<SearchResultKeywordScore> scores) {
int maxSet = 0;
for (var score : scores) {
maxSet = Math.max(maxSet, score.subquery);
}
return 1 + maxSet;
}
public static double normalize(double value, int setSize) {
if (value < 0)
value = 0;
return Math.sqrt((1.0 + scalingFactor) / (1.0 + value / Math.max(1., setSize)));
}
}
/** Pool of List instances used to reduce memory churn during result ranking in the index
* where potentially tens of thousands of candidate results are ranked.
*
* @param <T>
*/
@SuppressWarnings({"unchecked", "rawtypes"})
class ValuatorListPool<T> {
private final ArrayList[] items = new ArrayList[256];
public ValuatorListPool() {
for (int i = 0; i < items.length; i++) {
items[i] = new ArrayList();
}
}
public List<T> get(int i) {
var ret = (ArrayList<T>) items[i];
ret.clear();
return ret;
}
}

View File

@ -1,39 +1,80 @@
package nu.marginalia.ranking.factors;
import nu.marginalia.index.client.model.results.SearchResultRankingContext;
import nu.marginalia.index.client.model.results.Bm25Parameters;
import nu.marginalia.index.client.model.results.ResultRankingContext;
import nu.marginalia.model.idx.WordFlags;
import nu.marginalia.ranking.ResultKeywordSet;
/** This is a fairly coarse estimation of <a href="https://en.wikipedia.org/wiki/Okapi_BM25">BM-25</a>,
* since document count can't be accurately accessed at this point
*/
public class Bm25Factor {
private static final int AVG_LENGTH = 5000;
public double calculate(ResultKeywordSet keywordSet, int length, SearchResultRankingContext ctx) {
final double scalingFactor = 750.;
/** This is an estimation of <a href="https://en.wikipedia.org/wiki/Okapi_BM25">BM-25</a>.
*
* @see Bm25Parameters
*/
public double calculateBm25(Bm25Parameters bm25Parameters, ResultKeywordSet keywordSet, int length, ResultRankingContext ctx) {
final int docCount = ctx.termFreqDocCount();
final double wf1 = 0.7;
double k = 2;
if (length <= 0)
length = AVG_LENGTH;
double sum = 0.;
for (var keyword : keywordSet) {
for (var keyword : keywordSet.keywords()) {
double count = keyword.positionCount();
double wt = ctx.frequency(keyword.keyword);
int freq = ctx.frequency(keyword.keyword);
final double invFreq = Math.log(1.0 + (docCount - wt + 0.5)/(wt + 0.5));
sum += invFreq * (count * (k + 1)) / (count + k * (1 - wf1 + wf1 * AVG_LENGTH/length));
sum += invFreq(docCount, freq) * f(bm25Parameters.k(), bm25Parameters.b(), count, length);
}
double ret = Math.sqrt((1.0 + scalingFactor) / (1.0 + sum));
assert (Double.isFinite(ret));
return ret;
return sum;
}
/** Bm25 calculation, except instead of counting positions in the document,
* the number of relevance signals for the term is counted instead.
*/
public double calculateBm25Prio(Bm25Parameters bm25Parameters, ResultKeywordSet keywordSet, ResultRankingContext ctx) {
final int docCount = ctx.termFreqDocCount();
double sum = 0.;
long mask = WordFlags.Site.asBit()
| WordFlags.SiteAdjacent.asBit()
| WordFlags.UrlPath.asBit()
| WordFlags.UrlDomain.asBit()
| WordFlags.Subjects.asBit();
for (var keyword : keywordSet.keywords()) {
double count = Long.bitCount(keyword.encodedWordMetadata() & mask);
int freq = ctx.priorityFrequency(keyword.keyword);
sum += invFreq(docCount, freq) * f(bm25Parameters.k(), 0, count, 0);
}
return sum;
}
/**
*
* @param docCount Number of documents
* @param freq Number of matching documents
*/
private double invFreq(int docCount, int freq) {
return Math.log(1.0 + (docCount - freq + 0.5) / (freq + 0.5));
}
/**
*
* @param k determines the size of the impact of a single term
* @param b determines the magnitude of the length normalization
* @param count number of occurrences in the document
* @param length document length
*/
private double f(double k, double b, double count, int length) {
final double lengthRatio = (double) length / AVG_LENGTH;
return (count * (k + 1)) / (count + k * (1 - b + b * lengthRatio));
}
}

View File

@ -1,30 +1,20 @@
package nu.marginalia.ranking.factors;
import nu.marginalia.index.client.model.results.SearchResultKeywordScore;
import nu.marginalia.ranking.ResultKeywordSet;
import java.util.List;
/** Rewards results that have a priority term */
public class PriorityTermFactor {
public class PriorityTermBonus {
public double calculate(List<SearchResultKeywordScore> scores) {
for (var result : scores) {
if (result.hasPriorityTerms()) {
return 0.5;
return 2.0;
}
}
return 1.0;
return 0;
}
public double calculate(ResultKeywordSet set) {
for (var result : set) {
if (result.hasPriorityTerms()) {
return 0.5;
}
}
return 1.0;
}
}

View File

@ -2,33 +2,26 @@ package nu.marginalia.ranking.factors;
import nu.marginalia.ranking.ResultKeywordSet;
/** Rewards documents where terms appear frequently within the same sentences,
* and where this overlap is early in the document
/** Rewards documents where terms appear frequently within the same sentences
*/
public class TermCoherenceFactor {
public double calculate(ResultKeywordSet keywordSet) {
int mask = combinedMask(keywordSet);
long mask = combinedMask(keywordSet);
return bitsSetFactor(mask) * (0.8 + 0.2 * bitPositionFactor(mask));
return bitsSetFactor(mask);
}
double bitsSetFactor(int mask) {
final int bitsSetInMask = Integer.bitCount(mask);
double bitsSetFactor(long mask) {
final int bitsSetInMask = Long.bitCount(mask);
return Math.pow(bitsSetInMask/32.0, 0.25);
return Math.pow(bitsSetInMask/56., 0.25);
}
double bitPositionFactor(int mask) {
int start = Integer.numberOfTrailingZeros(mask);
long combinedMask(ResultKeywordSet keywordSet) {
long mask = 0xFF_FFFF_FFFF_FFFFL;
return 1 - (start)/32.0;
}
int combinedMask(ResultKeywordSet keywordSet) {
int mask = ~0;
for (var keyword : keywordSet) {
for (var keyword : keywordSet.keywords()) {
long positions = keyword.positions();
mask &= positions;

View File

@ -1,85 +0,0 @@
package nu.marginalia.ranking.factors;
import nu.marginalia.index.client.model.results.SearchResultKeywordScore;
import nu.marginalia.model.idx.WordFlags;
import nu.marginalia.ranking.ResultKeywordSet;
public class TermFlagsFactor {
public double calculate(ResultKeywordSet set, int titleLength) {
double totalFactorInvertSum = 0;
for (var keyword : set) {
double termFactor = calculateSingleTerm(keyword, titleLength);
assert (termFactor != 0.);
totalFactorInvertSum += 1 / (termFactor);
}
if (totalFactorInvertSum == 0.) {
return 1.;
}
return set.length() / totalFactorInvertSum;
}
public double calculateSingleTerm(SearchResultKeywordScore keyword, int titleLength) {
double f = 1.;
int posCount = keyword.positionCount();
final boolean title = keyword.hasTermFlag(WordFlags.Title);
final boolean site = keyword.hasTermFlag(WordFlags.Site);
final boolean siteAdjacent = keyword.hasTermFlag(WordFlags.SiteAdjacent);
final boolean urlDomain = keyword.hasTermFlag(WordFlags.UrlDomain);
final boolean urlPath = keyword.hasTermFlag(WordFlags.UrlPath);
final boolean names = keyword.hasTermFlag(WordFlags.NamesWords);
final boolean subject = keyword.hasTermFlag(WordFlags.Subjects);
if (title) {
f *= titleFactor(titleLength);
}
if (posCount != 0) {
if (site) {
f *= 0.75;
} else if (siteAdjacent) {
f *= 0.8;
}
if (subject) {
f *= 0.8;
}
else if (names) {
f *= 0.85;
}
}
assert (Double.isFinite(f));
if (urlDomain) {
f *= 0.8;
}
else if (urlPath && posCount > 1) {
f *= 0.9;
}
assert (Double.isFinite(f));
return f;
}
static double titleFactor(int titleLength) {
if (titleLength <= 64) {
return 0.5;
}
else if (titleLength < 96) {
return 0.75;
}
// likely keyword stuffing if the title is this long
return 0.9;
}
}

View File

@ -1,6 +1,7 @@
package nu.marginalia.ranking;
import nu.marginalia.index.client.model.results.SearchResultRankingContext;
import nu.marginalia.index.client.model.results.ResultRankingContext;
import nu.marginalia.index.client.model.results.ResultRankingParameters;
import nu.marginalia.index.client.model.results.SearchResultKeywordScore;
import nu.marginalia.model.idx.DocumentFlags;
import nu.marginalia.model.idx.WordFlags;
@ -13,10 +14,7 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import static org.mockito.Mockito.when;
@ -32,29 +30,28 @@ class ResultValuatorTest {
when(dict.docCount()).thenReturn(100_000);
valuator = new ResultValuator(
new TermFlagsFactor(),
new Bm25Factor(),
new TermCoherenceFactor(),
new PriorityTermFactor()
new PriorityTermBonus()
);
}
List<SearchResultKeywordScore> titleOnlyLowCountSet = List.of(
new SearchResultKeywordScore(0, "bob",
wordMetadata(32, Set.of(1), EnumSet.of(WordFlags.Title)),
wordMetadata(Set.of(1), EnumSet.of(WordFlags.Title)),
docMetadata(0, 2010, 0, 5, EnumSet.noneOf(DocumentFlags.class)),
false)
);
List<SearchResultKeywordScore> highCountNoTitleSet = List.of(
new SearchResultKeywordScore(0, "bob",
wordMetadata(129, Set.of(1,3,4,6,7,9,10,11,12,14,15,16), EnumSet.of(WordFlags.TfIdfHigh)),
wordMetadata(Set.of(1,3,4,6,7,9,10,11,12,14,15,16), EnumSet.of(WordFlags.TfIdfHigh)),
docMetadata(0, 2010, 0, 5, EnumSet.noneOf(DocumentFlags.class)),
false)
);
List<SearchResultKeywordScore> highCountSubjectSet = List.of(
new SearchResultKeywordScore(0, "bob",
wordMetadata(129, Set.of(1,3,4,6,7,9,10,11,12,14,15,16), EnumSet.of(WordFlags.TfIdfHigh, WordFlags.Subjects)),
wordMetadata(Set.of(1,3,4,6,7,9,10,11,12,14,15,16), EnumSet.of(WordFlags.TfIdfHigh, WordFlags.Subjects)),
docMetadata(0, 2010, 0, 5, EnumSet.noneOf(DocumentFlags.class)),
false)
);
@ -64,13 +61,14 @@ class ResultValuatorTest {
void evaluateTerms() {
when(dict.getTermFreq("bob")).thenReturn(10);
SearchResultRankingContext context = new SearchResultRankingContext(100000,
Map.of("bob", 10));
ResultRankingContext context = new ResultRankingContext(100000,
ResultRankingParameters.sensibleDefaults(),
Map.of("bob", 10), Collections.emptyMap());
double titleOnlyLowCount = valuator.calculateSearchResultValue(titleOnlyLowCountSet, 10_000, 32, context);
double titleLongOnlyLowCount = valuator.calculateSearchResultValue(titleOnlyLowCountSet, 10_000, 72, context);
double highCountNoTitle = valuator.calculateSearchResultValue(highCountNoTitleSet, 10_000, 32, context);
double highCountSubject = valuator.calculateSearchResultValue(highCountSubjectSet, 10_000, 32, context);
double titleOnlyLowCount = valuator.calculateSearchResultValue(titleOnlyLowCountSet, 10_000, context);
double titleLongOnlyLowCount = valuator.calculateSearchResultValue(titleOnlyLowCountSet, 10_000, context);
double highCountNoTitle = valuator.calculateSearchResultValue(highCountNoTitleSet, 10_000, context);
double highCountSubject = valuator.calculateSearchResultValue(highCountSubjectSet, 10_000, context);
System.out.println(titleOnlyLowCount);
System.out.println(titleLongOnlyLowCount);
@ -82,13 +80,13 @@ class ResultValuatorTest {
return new DocumentMetadata(topology, PubDate.toYearByte(year), sets, quality, flags).encode();
}
private long wordMetadata(int tfIdf, Set<Integer> positions, Set<WordFlags> wordFlags) {
int posBits = positions.stream()
.mapToInt(i -> (int)((1L << i) & 0xFFFF_FFFFL))
private long wordMetadata(Set<Integer> positions, Set<WordFlags> wordFlags) {
long posBits = positions.stream()
.mapToLong(i -> ((1L << i) & 0xFF_FFFF_FFFF_FFFFL))
.reduce((a,b) -> a|b)
.orElse(0);
.orElse(0L);
return new WordMetadata(tfIdf, posBits, wordFlags).encode();
return new WordMetadata(posBits, wordFlags).encode();
}
}

View File

@ -6,6 +6,7 @@ import nu.marginalia.model.idx.WordMetadata;
import nu.marginalia.ranking.ResultKeywordSet;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
@ -16,12 +17,11 @@ class TermCoherenceFactorTest {
@Test
public void testAllBitsSet() {
var allPositionsSet = createSet(
~0, ~0
0xFF_FFFF_FFFF_FFFFL, 0xFF_FFFF_FFFF_FFFFL
);
int mask = termCoherenceFactor.combinedMask(allPositionsSet);
long mask = termCoherenceFactor.combinedMask(allPositionsSet);
assertEquals(1.0, termCoherenceFactor.bitPositionFactor(mask), 0.01);
assertEquals(1.0, termCoherenceFactor.bitsSetFactor(mask), 0.01);
assertEquals(1.0, termCoherenceFactor.calculate(allPositionsSet));
@ -33,9 +33,8 @@ class TermCoherenceFactorTest {
0, 0
);
int mask = termCoherenceFactor.combinedMask(allPositionsSet);
long mask = termCoherenceFactor.combinedMask(allPositionsSet);
assertEquals(0, termCoherenceFactor.bitPositionFactor(mask), 0.01);
assertEquals(0, termCoherenceFactor.bitsSetFactor(mask), 0.01);
assertEquals(0, termCoherenceFactor.calculate(allPositionsSet));
@ -43,56 +42,54 @@ class TermCoherenceFactorTest {
@Test @SuppressWarnings("unchecked")
public void testLowPosMatches() {
var allPositionsSet = createSet(
var positions = createSet(
List.of(0, 1, 2, 3), List.of(0, 1, 2, 3)
);
int mask = termCoherenceFactor.combinedMask(allPositionsSet);
long mask = termCoherenceFactor.combinedMask(positions);
printMask(mask);
assertEquals(1.0, termCoherenceFactor.bitPositionFactor(mask), 0.01);
}
@Test @SuppressWarnings("unchecked")
public void testHiPosMatches() {
var allPositionsSet = createSet(
List.of(28, 29, 30, 31), List.of(28, 29, 30, 31)
var positions = createSet(
List.of(55, 54, 53, 52), List.of(55, 54, 53, 52)
);
int mask = termCoherenceFactor.combinedMask(allPositionsSet);
long mask = termCoherenceFactor.combinedMask(positions);
printMask(mask);
assertEquals(0.125, termCoherenceFactor.bitPositionFactor(mask), 0.01);
}
@Test
public void testBitMatchScaling() {
for (int i = 1; i < 32; i++) {
System.out.println(i + ":" + termCoherenceFactor.bitsSetFactor((1 << i) - 1));
for (int i = 1; i < 48; i++) {
System.out.println(i + ":" + termCoherenceFactor.bitsSetFactor((1L << i) - 1));
}
}
void printMask(int mask) {
System.out.println(BrailleBlockPunchCards.printBits(mask, 32));
void printMask(long mask) {
System.out.println(BrailleBlockPunchCards.printBits(mask, 48));
}
ResultKeywordSet createSet(List<Integer>... maskPositions) {
int[] positions = new int[maskPositions.length];
long[] positions = new long[maskPositions.length];
for (int i = 0; i < maskPositions.length; i++) {
for (int pos : maskPositions[i]) {
positions[i] |= (1<<pos);
for (long pos : maskPositions[i]) {
positions[i] |= (1L<<pos);
}
}
return createSet(positions);
}
ResultKeywordSet createSet(int... positionMasks) {
var keywords = new SearchResultKeywordScore[positionMasks.length];
ResultKeywordSet createSet(long... positionMasks) {
List<SearchResultKeywordScore> keywords = new ArrayList<>();
for (int i = 0; i < positionMasks.length; i++) {
keywords[i] = new SearchResultKeywordScore(0, "",
new WordMetadata(0, positionMasks[i], (byte) 0).encode(), 0, false);
keywords.add(new SearchResultKeywordScore(0, "",
new WordMetadata(positionMasks[i], (byte) 0).encode(), 0, false));
}
return new ResultKeywordSet(keywords);

View File

@ -18,7 +18,7 @@ public class BrailleBlockPunchCards {
* 8 "bits", but for historical reasons, they're addressed in a bit
* of an awkward way. Braille used to be a 2x6 grid, but it was extended
* to 2x8.
*
* It's addressed as follows
*
* 0 3

View File

@ -1,9 +1,13 @@
package nu.marginalia.language.sentence;
import nu.marginalia.WmsaHome;
import org.jsoup.Jsoup;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.Objects;
import static org.junit.jupiter.api.Assertions.*;
class SentenceExtractorTest {
@ -30,6 +34,47 @@ class SentenceExtractorTest {
assertEquals("uklanski", dld.wordsLowerCase[0]);
}
@Test
void testJava() {
var dld = sentenceExtractor.extractSentence("Foreign Function & Memory API");
assertEquals(4, dld.words.length);
assertArrayEquals(new String[] {"Foreign", "Function", "Memory", "API"}, dld.words);
}
@Test
void testJavaFile() {
try (var resource = Objects.requireNonNull(ClassLoader.getSystemResourceAsStream("html/jep.html"),
"Could not load word frequency table"))
{
var doc = Jsoup.parse(new String(resource.readAllBytes()));
var dld = sentenceExtractor.extractSentences(doc);
for (var sent : dld.sentences) {
System.out.println(sent);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Test
void testSpamFile() {
try (var resource = Objects.requireNonNull(ClassLoader.getSystemResourceAsStream("html/spam.html"),
"Could not load word frequency table"))
{
var doc = Jsoup.parse(new String(resource.readAllBytes()));
var dld = sentenceExtractor.extractSentences(doc);
for (var sent : dld.sentences) {
System.out.println(sent);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Test
void testApostrophe() {
var dld = sentenceExtractor.extractSentence("duke nuke 'em's big ol' big gun");

View File

@ -0,0 +1,927 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>OpenJDK Project JEPs</title>
<link rel="stylesheet" type="text/css" href="../style_jepmap.css" title="css">
</head>
<body>
<style>
.topmenu {
clear: none;
}
.menuitem {
padding-right: 16px;
float: left;
clear: none;
}
.menubutton_jep, .menubutton_explore, .menubutton_jit, .menubutton_book {
font-size: 14px;
font-weight: bold;
padding: 8px;
}
.menubutton_jep {
background-color: lightsteelblue;
}
.menubutton_explore {
background-color: mediumspringgreen;
}
.menubutton_jit {
background-color: darkorange;
}
.menubutton_book {
background-color: gold;
}
.clear {
float: none;
clear: both;
}
.topintro {
font-size: 16px;
padding: 4px 2px;
margin-bottom: 8px;
text-align: center;
border: solid 1px black;
background-color: #22ff22;
}
</style>
<div class="topintro">I make tools for understanding the Java Virtual Machine. Please support my work by <a
href="https://github.com/sponsors/chriswhocodes">sponsoring me on GitHub.</a> Thank you!
</div>
<div class="topmenu">
<div class="menuitem"><a href="https://chriswhocodes.com/fulljep.html">
<button class="menubutton_jep" title="Full-Text search for JEPs">FullJEP</button>
</a></div>
<div class="menuitem"><a href="https://chriswhocodes.com/jepmap.html">
<button class="menubutton_jep" title="A parser for discovering the relationships between OpenJDK Projects and their JEPs.">
JEPMap
</button>
</a></div>
<div class="menuitem"><a href="https://chriswhocodes.com/jepsearch.html">
<button class="menubutton_jep" title="Sort, search and filter JEPs">JEPSearch</button>
</a></div>
<div class="menuitem"><a href="https://chriswhocodes.com/hsdis/">
<button class="menubutton_jit" title="HotSpot Disassembly Plugin (hsdis)">hsdis</button>
</a></div>
<div class="menuitem"><a href="https://github.com/AdoptOpenJDK/jitwatch">
<button class="menubutton_jit"
title="Log analyser / visualiser for Java HotSpot JIT compiler. Inspect inlining decisions, hot methods, bytecode, and assembly.">
JITWatch
</button>
</a></div>
<div class="menuitem"><a href="https://jacoline.dev/inspect">
<button class="menubutton_explore" title="An interactive Java command line inspector.">JaCoLine</button>
</a></div>
<div class="menuitem"><a href="https://chriswhocodes.com/vm-options-explorer.html">
<button class="menubutton_explore" title="The complete list of JVM command line options for each JDK.">VM Options Explorer
</button>
</a></div>
<div class="menuitem"><a href="https://chriswhocodes.com/vm-intrinsics-explorer.html">
<button class="menubutton_explore" title="Inspect VM intrinsics for each JDK">VM Intrinsics Explorer</button>
</a></div>
<div class="menuitem"><a href="https://chriswhocodes.com/gc-explorer.html">
<button class="menubutton_explore" title="Explorer GC options for each JDK">GC Explorer</button>
</a></div>
<div class="menuitem"><a href="https://optimizingjava.com">
<button class="menubutton_book" title="Learn how the JVM really works and how to optimise your code and runtime settings">
Optimizing Java
</button>
</a></div>
</div>
<div class="clear"></div>
<script data-host="https://microanalytics.io" data-dnt="false" src="https://microanalytics.io/js/script.js" id="ZwSg9rf6GA" async defer></script>
<h1>OpenJDK Project JEPs (JDK Enhancement Proposals)</h1>
<div class="intro">
<div>Built using <a href="https://github.com/chriswhocodes/JEPMap">JEPMap</a> by <a
href="https://twitter.com/chriswhocodes">@chriswhocodes</a>.
Last updated: 2023-04-04
</div>
</div>
<div class="algo">The algorithm for generating this page is:
<ol>
<li>Parse the JEP pages at <a href="https://openjdk.java.net/jeps/">https://openjdk.java.net/jeps</a></li>
<li>For each Project listed at <a href="https://openjdk.java.net/projects/">https://openjdk.java.net/projects/</a>
<ol>
<li>Parse the project page and wiki page looking for JEP URLs</li>
<li>Check if the JEP page discussion mailing list name matches a Project</li>
<li>Check if the JEP page text links to a Project</li>
<li>Map JEP 'Release' tag back to a JDK (technically a JDK is not a Project but I think it's useful to list the
JEPs
targeted to a JDK)
</li>
<li>Remove mappings found in the <a
href="https://github.com/chriswhocodes/JEPMap/blob/main/src/main/resources/badmappings.properties">banlist</a>
</li>
</ol>
</li>
</ol>
<div class="warn">Autogeneration can produce false positives! Please report issues at <a
href="https://github.com/chriswhocodes/JEPMap/issues">https://github.com/chriswhocodes/JEPMap/issues</a></div>
</div>
<div class="jump_container">
<div class="jump"><a href="#amber">Amber</a></div>
<div class="jump"><a href="#code-tools">Code Tools</a></div>
<div class="jump"><a href="#graal">Graal</a></div>
<div class="jump"><a href="#graphics-rasterizer">Graphics Rasterizer</a></div>
<div class="jump"><a href="#jdk7">JDK7</a></div>
<div class="jump"><a href="#jdk8">JDK8</a></div>
<div class="jump"><a href="#jdk9">JDK9</a></div>
<div class="jump"><a href="#jdk/10">JDK10</a></div>
<div class="jump"><a href="#jdk/11">JDK11</a></div>
<div class="jump"><a href="#jdk/12">JDK12</a></div>
<div class="jump"><a href="#jdk/13">JDK13</a></div>
<div class="jump"><a href="#jdk/14">JDK14</a></div>
<div class="jump"><a href="#jdk/15">JDK15</a></div>
<div class="jump"><a href="#jdk/16">JDK16</a></div>
<div class="jump"><a href="#jdk/17">JDK17</a></div>
<div class="jump"><a href="#jdk/18">JDK18</a></div>
<div class="jump"><a href="#jdk/19">JDK19</a></div>
<div class="jump"><a href="#jdk/20">JDK20</a></div>
<div class="jump"><a href="#jigsaw">Jigsaw</a></div>
<div class="jump"><a href="#kulla">Kulla</a></div>
<div class="jump"><a href="#lambda">Lambda</a></div>
<div class="jump"><a href="#lanai">Lanai</a></div>
<div class="jump"><a href="#locale-enhancement">Locale Enhancement</a></div>
<div class="jump"><a href="#loom">Loom</a></div>
<div class="jump"><a href="#jmm">Memory Model Update</a></div>
<div class="jump"><a href="#mlvm">Multi-Language VM</a></div>
<div class="jump"><a href="#nashorn">Nashorn</a></div>
<div class="jump"><a href="#nio">New I/O</a></div>
<div class="jump"><a href="#openjfx">OpenJFX</a></div>
<div class="jump"><a href="#panama">Panama</a></div>
<div class="jump"><a href="#aarch32-port">Port: AArch32</a></div>
<div class="jump"><a href="#aarch64-port">Port: AArch64</a></div>
<div class="jump"><a href="#ppc-aix-port">Port: PowerPC/AIX</a></div>
<div class="jump"><a href="#riscv-port">Port: RISC-V</a></div>
<div class="jump"><a href="#s390x-port">Port: s390x</a></div>
<div class="jump"><a href="#portola">Portola</a></div>
<div class="jump"><a href="#shenandoah">Shenandoah</a></div>
<div class="jump"><a href="#skara">Skara</a></div>
<div class="jump"><a href="#tiered-attrib">Tiered Attribution</a></div>
<div class="jump"><a href="#type-annotations">Type Annotations</a></div>
<div class="jump"><a href="#valhalla">Valhalla</a></div>
<div class="jump"><a href="#verona">Verona</a></div>
<div class="jump"><a href="#zgc">ZGC</a></div>
<div class="clear"></div>
</div>
<div class="project" id="amber">
<h2><a href="https://openjdk.java.net/projects/amber">Amber</a></h2>
<div class="description">The goal of Project Amber is to explore and incubate smaller, productivity-oriented Java language features that have been accepted as candidate JEPs in the OpenJDK JEP Process. This Project is sponsored by the Compiler Group.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/286">JEP 286: Local-Variable Type Inference</a><div class="jepstatus">[Release: 10] [Status: Closed/Delivered] [Updated: 2022/09/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/301">JEP 301: Enhanced Enums</a><div class="jepstatus">[Status: Closed/Withdrawn] [Updated: 2020/09/29]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/302">JEP 302: Lambda Leftovers</a><div class="jepstatus">[Status: Candidate] [Updated: 2017/04/11]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/305">JEP 305: Pattern Matching for instanceof (Preview)</a><div class="jepstatus">[Release: 14] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/309">JEP 309: Dynamic Class-File Constants</a><div class="jepstatus">[Release: 11] [Status: Closed/Delivered] [Updated: 2018/09/10]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/323">JEP 323: Local-Variable Syntax for Lambda Parameters</a><div class="jepstatus">[Release: 11] [Status: Closed/Delivered] [Updated: 2018/08/23]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/325">JEP 325: Switch Expressions (Preview)</a><div class="jepstatus">[Release: 12] [Status: Closed/Delivered] [Updated: 2022/03/11]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/326">JEP 326: Raw String Literals (Preview)</a><div class="jepstatus">[Status: Closed/Withdrawn] [Updated: 2020/05/01]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/334">JEP 334: JVM Constants API</a><div class="jepstatus">[Release: 12] [Status: Closed/Delivered] [Updated: 2022/08/02]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/348">JEP 348: Compiler Intrinsics for Java SE APIs</a><div class="jepstatus">[Status: Candidate] [Updated: 2023/02/10]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/354">JEP 354: Switch Expressions (Second Preview)</a><div class="jepstatus">[Release: 13] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/355">JEP 355: Text Blocks (Preview)</a><div class="jepstatus">[Release: 13] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/359">JEP 359: Records (Preview)</a><div class="jepstatus">[Release: 14] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/360">JEP 360: Sealed Classes (Preview)</a><div class="jepstatus">[Release: 15] [Status: Closed/Delivered] [Updated: 2022/03/11]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/361">JEP 361: Switch Expressions</a><div class="jepstatus">[Release: 14] [Status: Closed/Delivered] [Updated: 2022/03/11]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/368">JEP 368: Text Blocks (Second Preview)</a><div class="jepstatus">[Release: 14] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/375">JEP 375: Pattern Matching for instanceof (Second Preview)</a><div class="jepstatus">[Release: 15] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/378">JEP 378: Text Blocks</a><div class="jepstatus">[Release: 15] [Status: Closed/Delivered] [Updated: 2020/07/30]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/384">JEP 384: Records (Second Preview)</a><div class="jepstatus">[Release: 15] [Status: Closed/Delivered] [Updated: 2022/03/11]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/394">JEP 394: Pattern Matching for instanceof</a><div class="jepstatus">[Release: 16] [Status: Closed/Delivered] [Updated: 2022/06/10]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/395">JEP 395: Records</a><div class="jepstatus">[Release: 16] [Status: Closed/Delivered] [Updated: 2022/02/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/397">JEP 397: Sealed Classes (Second Preview)</a><div class="jepstatus">[Release: 16] [Status: Closed/Delivered] [Updated: 2022/03/11]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/405">JEP 405: Record Patterns (Preview)</a><div class="jepstatus">[Release: 19] [Status: Closed/Delivered] [Updated: 2022/10/06]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/406">JEP 406: Pattern Matching for switch (Preview)</a><div class="jepstatus">[Release: 17] [Status: Closed/Delivered] [Updated: 2022/06/01]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/409">JEP 409: Sealed Classes</a><div class="jepstatus">[Release: 17] [Status: Closed/Delivered] [Updated: 2022/03/19]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/420">JEP 420: Pattern Matching for switch (Second Preview)</a><div class="jepstatus">[Release: 18] [Status: Closed/Delivered] [Updated: 2022/03/22]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/427">JEP 427: Pattern Matching for switch (Third Preview)</a><div class="jepstatus">[Release: 19] [Status: Closed/Delivered] [Updated: 2022/10/06]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/430">JEP 430: String Templates (Preview)</a><div class="jepstatus">[Release: 21] [Status: Proposed to Target] [Updated: 2023/03/31]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/432">JEP 432: Record Patterns (Second Preview)</a><div class="jepstatus">[Release: 20] [Status: Closed/Delivered] [Updated: 2023/03/02]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/433">JEP 433: Pattern Matching for switch (Fourth Preview)</a><div class="jepstatus">[Release: 20] [Status: Closed/Delivered] [Updated: 2023/03/21]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/440">JEP 440: Record Patterns</a><div class="jepstatus">[Status: Candidate] [Updated: 2023/03/17]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/441">JEP 441: Pattern Matching for switch</a><div class="jepstatus">[Status: Candidate] [Updated: 2023/03/17]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/443">JEP 443: Unnamed Patterns and Variables (Preview)</a><div class="jepstatus">[Status: Candidate] [Updated: 2023/03/21]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/8209434">JEP 8209434: JEP draft: Concise Method Bodies</a><div class="jepstatus">[Status: Draft] [Updated: 2019/03/25]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/8288476">JEP 8288476: JEP draft: Primitive types in patterns, instanceof, and switch</a><div class="jepstatus">[Status: Submitted] [Updated: 2023/03/21]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/8300786">JEP 8300786: JEP draft: Statements before super()</a><div class="jepstatus">[Status: Submitted] [Updated: 2023/03/21]</div></div>
</div>
</div>
<div class="project" id="code-tools">
<h2><a href="https://openjdk.java.net/projects/code-tools">Code Tools</a></h2>
<div class="description">The goal of this Project is to provide tools of use to developers who work on the OpenJDK code base. Such tools currently include test tools and Mercurial extensions; it is envisaged that additional tools will be added over time, after discussion on the Project's main mailing list and subject to the Project Lead's approval.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/193">JEP 193: Variable Handles</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/08/17]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/8249196">JEP 8249196: JEP draft: Low-level Object layout introspection methods</a><div class="jepstatus">[Status: Draft] [Updated: 2023/01/05]</div></div>
</div>
</div>
<div class="project" id="graal">
<h2><a href="https://openjdk.java.net/projects/graal">Graal</a></h2>
<div class="description">The Graal OpenJDK project grew out of the Maxine VM project. In the context of the Maxine VM, Graal demonstrated that a compiler written in Java (with all its software engineering advantages) could generate highly optimized code without compromising on compile times.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/243">JEP 243: Java-Level JVM Compiler Interface</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2019/09/16]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/295">JEP 295: Ahead-of-Time Compilation</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2018/10/05]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/317">JEP 317: Experimental Java-Based JIT Compiler</a><div class="jepstatus">[Release: 10] [Status: Closed/Delivered] [Updated: 2018/03/28]</div></div>
</div>
</div>
<div class="project" id="graphics-rasterizer">
<h2><a href="https://openjdk.java.net/projects/graphics-rasterizer">Graphics Rasterizer</a></h2>
<div class="description">Due to encumbrances in the 2D source code (see the 2D Graphics page for more), some of the implementation of the Java 2D API requires open source replacements.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/265">JEP 265: Marlin Graphics Renderer</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/02/27]</div></div>
</div>
</div>
<div class="project" id="jdk7">
<h2><a href="https://openjdk.java.net/projects/jdk7">JDK7</a></h2>
<div class="description">The primary goal of this Project was to produce an open-source implementation of the seventh edition of the Java SE Platform, as defined by JSR 336 in the Java Community Process.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/167">JEP 167: Event-Based JVM Tracing</a><div class="jepstatus">[Release: 7u40] [Status: Closed/Delivered] [Updated: 2019/08/15]</div></div>
</div>
</div>
<div class="project" id="jdk8">
<h2><a href="https://openjdk.java.net/projects/jdk8">JDK8</a></h2>
<div class="description">The goal of this Project was to produce an open-source reference implementation of the Java SE 8 Platform Specification defined by JSR 337 in the Java Community Process.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/101">JEP 101: Generalized Target-Type Inference</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2015/02/26]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/103">JEP 103: Parallel Array Sorting</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2017/08/13]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/104">JEP 104: Type Annotations</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2020/06/01]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/105">JEP 105: DocTree API</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2015/02/13]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/106">JEP 106: Add Javadoc to javax.tools</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2015/02/13]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/107">JEP 107: Bulk Data Operations for Collections</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2014/07/10]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/109">JEP 109: Enhance Core Libraries with Lambda</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2015/02/13]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/112">JEP 112: Charset Implementation Improvements</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2015/01/22]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/113">JEP 113: MS-SFU Kerberos 5 Extensions</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2015/02/12]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/114">JEP 114: TLS Server Name Indication (SNI) Extension</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2017/06/14]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/115">JEP 115: AEAD CipherSuites</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2017/08/11]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/117">JEP 117: Remove the Annotation-Processing Tool (apt)</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2014/11/03]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/118">JEP 118: Access to Parameter Names at Runtime</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2015/02/13]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/119">JEP 119: javax.lang.model Implementation Backed by Core Reflection</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2015/02/13]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/120">JEP 120: Repeating Annotations</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2015/02/13]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/121">JEP 121: Stronger Algorithms for Password-Based Encryption</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2017/06/02]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/122">JEP 122: Remove the Permanent Generation</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2014/08/06]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/123">JEP 123: Configurable Secure Random-Number Generation</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2017/08/11]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/124">JEP 124: Enhance the Certificate Revocation-Checking API</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2014/07/10]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/126">JEP 126: Lambda Expressions & Virtual Extension Methods</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2015/01/09]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/127">JEP 127: Improve Locale Data Packaging and Adopt Unicode CLDR Data</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2016/04/04]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/128">JEP 128: Unicode BCP 47 Locale Matching</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2017/10/23]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/129">JEP 129: NSA Suite B Cryptographic Algorithms</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2017/06/14]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/130">JEP 130: SHA-224 Message Digests</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2017/06/15]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/131">JEP 131: PKCS#11 Crypto Provider for 64-bit Windows</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2017/06/14]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/133">JEP 133: Unicode 6.2</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2019/05/13]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/135">JEP 135: Base64 Encoding & Decoding</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2017/06/14]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/136">JEP 136: Enhanced Verification Errors</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2017/06/14]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/138">JEP 138: Autoconf-Based Build System</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2015/05/11]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/139">JEP 139: Enhance javac to Improve Build Speed</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2015/02/13]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/140">JEP 140: Limited doPrivileged</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2014/07/10]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/142">JEP 142: Reduce Cache Contention on Specified Fields</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2017/06/15]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/147">JEP 147: Reduce Class Metadata Footprint</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2014/08/08]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/148">JEP 148: Small VM</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2017/06/14]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/149">JEP 149: Reduce Core-Library Memory Usage</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2016/02/18]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/150">JEP 150: Date & Time API</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2015/01/22]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/153">JEP 153: Launch JavaFX Applications</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2015/05/01]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/155">JEP 155: Concurrency Updates</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2017/08/13]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/156">JEP 156: G1 GC: Reduce need for full GCs</a><div class="jepstatus">[Release: 8u40] [Status: Closed/Delivered] [Updated: 2015/02/26]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/160">JEP 160: Lambda-Form Representation for Method Handles</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2017/10/17]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/161">JEP 161: Compact Profiles</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2023/01/26]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/162">JEP 162: Prepare for Modularization</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2017/06/14]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/164">JEP 164: Leverage CPU Instructions for AES Cryptography</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2017/06/15]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/166">JEP 166: Overhaul JKS-JCEKS-PKCS12 Keystores</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2022/06/14]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/168">JEP 168: Network Discovery of Manageable Java Processes</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2016/06/07]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/170">JEP 170: JDBC 4.2</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2016/11/01]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/171">JEP 171: Fence Intrinsics</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2017/06/14]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/172">JEP 172: DocLint</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2016/06/07]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/173">JEP 173: Retire Some Rarely-Used GC Combinations</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2018/06/19]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/174">JEP 174: Nashorn JavaScript Engine</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2015/02/13]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/175">JEP 175: PowerPC/AIX Port</a><div class="jepstatus">[Release: 8u20] [Status: Closed/Delivered] [Updated: 2017/08/17]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/176">JEP 176: Mechanical Checking of Caller-Sensitive Methods</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2017/10/17]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/177">JEP 177: Optimize java.text.DecimalFormat.format</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2014/11/03]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/178">JEP 178: Statically-Linked JNI Libraries</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2016/06/07]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/179">JEP 179: Document JDK API Support and Stability</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2014/11/03]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/180">JEP 180: Handle Frequent HashMap Collisions with Balanced Trees</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2017/06/14]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/184">JEP 184: HTTP URL Permissions</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2017/06/14]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/185">JEP 185: Restrict Fetching of External XML Resources</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2017/06/14]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/192">JEP 192: String Deduplication in G1</a><div class="jepstatus">[Release: 8u20] [Status: Closed/Delivered] [Updated: 2017/06/07]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/195">JEP 195: Scalable Native Memory Tracking</a><div class="jepstatus">[Release: 8u40] [Status: Closed/Delivered] [Updated: 2015/02/27]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/196">JEP 196: Nashorn Optimistic Typing</a><div class="jepstatus">[Release: 8u40] [Status: Closed/Delivered] [Updated: 2014/12/05]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/202">JEP 202: Nashorn Class Filter</a><div class="jepstatus">[Release: 8u40] [Status: Closed/Delivered] [Updated: 2017/05/17]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/203">JEP 203: Nashorn: Lexically-Scoped Variable & Constant Declarations</a><div class="jepstatus">[Release: 8u40] [Status: Closed/Delivered] [Updated: 2017/05/17]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/204">JEP 204: JavaFX Accessibility</a><div class="jepstatus">[Release: 8u40] [Status: Closed/Delivered] [Updated: 2016/06/07]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/205">JEP 205: New Controls for JavaFX</a><div class="jepstatus">[Release: 8u40] [Status: Closed/Delivered] [Updated: 2015/03/03]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/206">JEP 206: Modernize the JavaFX Media Stack on Mac OS X</a><div class="jepstatus">[Release: 8u40] [Status: Closed/Delivered] [Updated: 2015/02/26]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/207">JEP 207: Leverage CPU Instructions to Improve SHA Performance on SPARC</a><div class="jepstatus">[Release: 8u40] [Status: Closed/Delivered] [Updated: 2014/10/06]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/208">JEP 208: Java Packager Improvements</a><div class="jepstatus">[Release: 8u40] [Status: Closed/Delivered] [Updated: 2015/02/26]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/209">JEP 209: JavaFX Scene Builder Update</a><div class="jepstatus">[Release: 8u40] [Status: Closed/Delivered] [Updated: 2014/10/01]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/210">JEP 210: LambdaForm Reduction and Caching</a><div class="jepstatus">[Release: 8u40] [Status: Closed/Delivered] [Updated: 2015/02/27]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/239">JEP 239: Update JavaFX/WebView to Newer Version of WebKit</a><div class="jepstatus">[Release: 8u60] [Status: Closed/Delivered] [Updated: 2016/08/24]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/242">JEP 242: JVM Trace Events for Failed Allocations</a><div class="jepstatus">[Release: 8u60] [Status: Closed/Delivered] [Updated: 2015/06/25]</div></div>
</div>
</div>
<div class="project" id="jdk9">
<h2><a href="https://openjdk.java.net/projects/jdk9">JDK9</a></h2>
<div class="description">The goal of this Project was to produce an open-source reference implementation of the Java SE 9 Platform as defined by JSR 379 in the Java Community Process.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/102">JEP 102: Process API Updates</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/03/09]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/110">JEP 110: HTTP/2 Client (Incubator)</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2022/05/31]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/143">JEP 143: Improve Contended Locking</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/03/06]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/158">JEP 158: Unified JVM Logging</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2022/10/05]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/165">JEP 165: Compiler Control</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2020/03/24]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/193">JEP 193: Variable Handles</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/08/17]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/197">JEP 197: Segmented Code Cache</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/04/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/199">JEP 199: Smart Java Compilation, Phase Two</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2016/07/12]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/200">JEP 200: The Modular JDK</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/09/21]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/201">JEP 201: Modular Source Code</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2020/12/07]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/211">JEP 211: Elide Deprecation Warnings on Import Statements</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2021/03/20]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/212">JEP 212: Resolve Lint and Doclint Warnings</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2021/03/02]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/213">JEP 213: Milling Project Coin</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/03/09]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/214">JEP 214: Remove GC Combinations Deprecated in JDK 8</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/01/25]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/215">JEP 215: Tiered Attribution for javac</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2016/07/12]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/216">JEP 216: Process Import Statements Correctly</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2016/07/12]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/217">JEP 217: Annotations Pipeline 2.0</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2016/07/12]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/219">JEP 219: Datagram Transport Layer Security (DTLS)</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2021/07/15]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/220">JEP 220: Modular Run-Time Images</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/09/22]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/221">JEP 221: New Doclet API</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/08/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/222">JEP 222: jshell: The Java Shell (Read-Eval-Print Loop)</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/06/09]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/223">JEP 223: New Version-String Scheme</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2021/10/03]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/224">JEP 224: HTML5 Javadoc</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2018/04/13]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/225">JEP 225: Javadoc Search</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/06/05]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/226">JEP 226: UTF-8 Property Resource Bundles</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2022/01/14]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/227">JEP 227: Unicode 7.0</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2016/12/09]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/228">JEP 228: Add More Diagnostic Commands</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/03/07]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/229">JEP 229: Create PKCS12 Keystores by Default</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2018/01/11]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/231">JEP 231: Remove Launch-Time JRE Version Selection</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/03/09]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/232">JEP 232: Improve Secure Application Performance</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/03/10]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/233">JEP 233: Generate Run-Time Compiler Tests Automatically</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/04/10]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/235">JEP 235: Test Class-File Attributes Generated by javac</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2016/10/10]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/236">JEP 236: Parser API for Nashorn</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/05/17]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/237">JEP 237: Linux/AArch64 Port</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/03/08]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/238">JEP 238: Multi-Release JAR Files</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/06/22]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/240">JEP 240: Remove the JVM TI hprof Agent</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2016/06/07]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/241">JEP 241: Remove the jhat Tool</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2016/09/06]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/243">JEP 243: Java-Level JVM Compiler Interface</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2019/09/16]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/244">JEP 244: TLS Application-Layer Protocol Negotiation Extension</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2022/08/10]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/245">JEP 245: Validate JVM Command-Line Flag Arguments</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2018/04/23]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/246">JEP 246: Leverage CPU Instructions for GHASH and RSA</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/03/06]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/247">JEP 247: Compile for Older Platform Versions</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2020/12/21]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/248">JEP 248: Make G1 the Default Garbage Collector</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/09/12]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/249">JEP 249: OCSP Stapling for TLS</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2020/07/03]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/250">JEP 250: Store Interned Strings in CDS Archives</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2022/10/03]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/251">JEP 251: Multi-Resolution Images</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/06/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/252">JEP 252: Use CLDR Locale Data by Default</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/10/23]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/253">JEP 253: Prepare JavaFX UI Controls & CSS APIs for Modularization</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/03/10]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/254">JEP 254: Compact Strings</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2022/04/11]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/255">JEP 255: Merge Selected Xerces 2.11.0 Updates into JAXP</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/05/26]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/256">JEP 256: BeanInfo Annotations</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/02/23]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/257">JEP 257: Update JavaFX/Media to Newer Version of GStreamer</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2022/11/21]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/258">JEP 258: HarfBuzz Font-Layout Engine</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2019/10/25]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/259">JEP 259: Stack-Walking API</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/07/18]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/260">JEP 260: Encapsulate Most Internal APIs</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/09/25]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/261">JEP 261: Module System</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/09/22]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/262">JEP 262: TIFF Image I/O</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/02/23]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/263">JEP 263: HiDPI Graphics on Windows and Linux</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/02/23]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/264">JEP 264: Platform Logging API and Service</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/07/24]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/265">JEP 265: Marlin Graphics Renderer</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/02/27]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/266">JEP 266: More Concurrency Updates</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/04/24]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/267">JEP 267: Unicode 8.0</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2019/05/13]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/268">JEP 268: XML Catalogs</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2019/04/08]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/269">JEP 269: Convenience Factory Methods for Collections</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/06/26]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/270">JEP 270: Reserved Stack Areas for Critical Sections</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2023/01/24]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/271">JEP 271: Unified GC Logging</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/06/02]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/272">JEP 272: Platform-Specific Desktop Features</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/06/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/273">JEP 273: DRBG-Based SecureRandom Implementations</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2019/11/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/274">JEP 274: Enhanced Method Handles</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/05/17]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/275">JEP 275: Modular Java Application Packaging</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/04/27]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/276">JEP 276: Dynamic Linking of Language-Defined Object Models</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/05/17]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/277">JEP 277: Enhanced Deprecation</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/12/08]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/278">JEP 278: Additional Tests for Humongous Objects in G1</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/04/10]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/279">JEP 279: Improve Test-Failure Troubleshooting</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2023/03/14]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/280">JEP 280: Indify String Concatenation</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2022/04/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/281">JEP 281: HotSpot C++ Unit-Test Framework</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/04/10]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/282">JEP 282: jlink: The Java Linker</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/05/19]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/283">JEP 283: Enable GTK 3 on Linux</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2018/10/12]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/284">JEP 284: New HotSpot Build System</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/01/20]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/285">JEP 285: Spin-Wait Hints</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2023/01/10]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/287">JEP 287: SHA-3 Hash Algorithms</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/03/06]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/288">JEP 288: Disable SHA-1 Certificates</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/11/20]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/289">JEP 289: Deprecate the Applet API</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2020/11/10]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/290">JEP 290: Filter Incoming Serialization Data</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2022/08/15]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/291">JEP 291: Deprecate the Concurrent Mark Sweep (CMS) Garbage Collector</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2020/04/06]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/292">JEP 292: Implement Selected ECMAScript 6 Features in Nashorn</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/03/09]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/294">JEP 294: Linux/s390x Port</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2019/08/13]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/295">JEP 295: Ahead-of-Time Compilation</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2018/10/05]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/296">JEP 296: Consolidate the JDK Forest into a Single Repository</a><div class="jepstatus">[Release: 10] [Status: Closed/Delivered] [Updated: 2019/11/07]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/297">JEP 297: Unified arm32/arm64 Port</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2022/08/11]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/298">JEP 298: Remove Demos and Samples</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/05/08]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/299">JEP 299: Reorganize Documentation</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/07/20]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/8068562">JEP 8068562: JEP draft: javadoc tags to distinguish API, implementation, specification, and notes</a><div class="jepstatus">[Status: Draft] [Updated: 2021/03/26]</div></div>
</div>
</div>
<div class="project" id="jdk/10">
<h2><a href="https://openjdk.java.net/projects/jdk/10">JDK10</a></h2>
<div class="description">JDK 10 is the open-source reference implementation of the Java SE 10 Platform as defined by JSR 383 in the Java Community Process.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/286">JEP 286: Local-Variable Type Inference</a><div class="jepstatus">[Release: 10] [Status: Closed/Delivered] [Updated: 2022/09/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/296">JEP 296: Consolidate the JDK Forest into a Single Repository</a><div class="jepstatus">[Release: 10] [Status: Closed/Delivered] [Updated: 2019/11/07]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/304">JEP 304: Garbage Collector Interface</a><div class="jepstatus">[Release: 10] [Status: Closed/Delivered] [Updated: 2018/04/09]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/307">JEP 307: Parallel Full GC for G1</a><div class="jepstatus">[Release: 10] [Status: Closed/Delivered] [Updated: 2018/03/29]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/310">JEP 310: Application Class-Data Sharing</a><div class="jepstatus">[Release: 10] [Status: Closed/Delivered] [Updated: 2022/10/03]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/312">JEP 312: Thread-Local Handshakes</a><div class="jepstatus">[Release: 10] [Status: Closed/Delivered] [Updated: 2019/08/21]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/313">JEP 313: Remove the Native-Header Generation Tool (javah)</a><div class="jepstatus">[Release: 10] [Status: Closed/Delivered] [Updated: 2018/01/10]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/314">JEP 314: Additional Unicode Language-Tag Extensions</a><div class="jepstatus">[Release: 10] [Status: Closed/Delivered] [Updated: 2018/03/06]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/316">JEP 316: Heap Allocation on Alternative Memory Devices</a><div class="jepstatus">[Release: 10] [Status: Closed/Delivered] [Updated: 2020/10/02]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/317">JEP 317: Experimental Java-Based JIT Compiler</a><div class="jepstatus">[Release: 10] [Status: Closed/Delivered] [Updated: 2018/03/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/319">JEP 319: Root Certificates</a><div class="jepstatus">[Release: 10] [Status: Closed/Delivered] [Updated: 2018/08/14]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/322">JEP 322: Time-Based Release Versioning</a><div class="jepstatus">[Release: 10] [Status: Closed/Delivered] [Updated: 2021/01/06]</div></div>
</div>
</div>
<div class="project" id="jdk/11">
<h2><a href="https://openjdk.java.net/projects/jdk/11">JDK11</a></h2>
<div class="description">JDK 11 is the open-source reference implementation of version 11 of the Java SE Platform as specified by by JSR 384 in the Java Community Process.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/181">JEP 181: Nest-Based Access Control</a><div class="jepstatus">[Release: 11] [Status: Closed/Delivered] [Updated: 2021/04/24]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/309">JEP 309: Dynamic Class-File Constants</a><div class="jepstatus">[Release: 11] [Status: Closed/Delivered] [Updated: 2018/09/10]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/315">JEP 315: Improve Aarch64 Intrinsics</a><div class="jepstatus">[Release: 11] [Status: Closed/Delivered] [Updated: 2018/09/10]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/318">JEP 318: Epsilon: A No-Op Garbage Collector (Experimental)</a><div class="jepstatus">[Release: 11] [Status: Closed/Delivered] [Updated: 2018/09/24]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/320">JEP 320: Remove the Java EE and CORBA Modules</a><div class="jepstatus">[Release: 11] [Status: Closed/Delivered] [Updated: 2019/05/23]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/321">JEP 321: HTTP Client</a><div class="jepstatus">[Release: 11] [Status: Closed/Delivered] [Updated: 2023/03/15]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/323">JEP 323: Local-Variable Syntax for Lambda Parameters</a><div class="jepstatus">[Release: 11] [Status: Closed/Delivered] [Updated: 2018/08/23]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/324">JEP 324: Key Agreement with Curve25519 and Curve448</a><div class="jepstatus">[Release: 11] [Status: Closed/Delivered] [Updated: 2018/09/13]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/327">JEP 327: Unicode 10</a><div class="jepstatus">[Release: 11] [Status: Closed/Delivered] [Updated: 2018/08/07]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/328">JEP 328: Flight Recorder</a><div class="jepstatus">[Release: 11] [Status: Closed/Delivered] [Updated: 2018/09/09]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/329">JEP 329: ChaCha20 and Poly1305 Cryptographic Algorithms</a><div class="jepstatus">[Release: 11] [Status: Closed/Delivered] [Updated: 2018/09/13]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/330">JEP 330: Launch Single-File Source-Code Programs</a><div class="jepstatus">[Release: 11] [Status: Closed/Delivered] [Updated: 2023/03/17]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/331">JEP 331: Low-Overhead Heap Profiling</a><div class="jepstatus">[Release: 11] [Status: Closed/Delivered] [Updated: 2018/09/05]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/332">JEP 332: Transport Layer Security (TLS) 1.3</a><div class="jepstatus">[Release: 11] [Status: Closed/Delivered] [Updated: 2018/09/17]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/333">JEP 333: ZGC: A Scalable Low-Latency Garbage Collector (Experimental)</a><div class="jepstatus">[Release: 11] [Status: Closed/Delivered] [Updated: 2020/03/13]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/335">JEP 335: Deprecate the Nashorn JavaScript Engine</a><div class="jepstatus">[Release: 11] [Status: Closed/Delivered] [Updated: 2020/04/06]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/336">JEP 336: Deprecate the Pack200 Tools and API</a><div class="jepstatus">[Release: 11] [Status: Closed/Delivered] [Updated: 2020/02/22]</div></div>
</div>
</div>
<div class="project" id="jdk/12">
<h2><a href="https://openjdk.java.net/projects/jdk/12">JDK12</a></h2>
<div class="description">JDK 12 is the open-source reference implementation of version 12 of the Java SE Platform as specified by by JSR 386 in the Java Community Process.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/189">JEP 189: Shenandoah: A Low-Pause-Time Garbage Collector (Experimental)</a><div class="jepstatus">[Release: 12] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/230">JEP 230: Microbenchmark Suite</a><div class="jepstatus">[Release: 12] [Status: Closed/Delivered] [Updated: 2019/02/27]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/325">JEP 325: Switch Expressions (Preview)</a><div class="jepstatus">[Release: 12] [Status: Closed/Delivered] [Updated: 2022/03/11]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/334">JEP 334: JVM Constants API</a><div class="jepstatus">[Release: 12] [Status: Closed/Delivered] [Updated: 2022/08/02]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/340">JEP 340: One AArch64 Port, Not Two</a><div class="jepstatus">[Release: 12] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/341">JEP 341: Default CDS Archives</a><div class="jepstatus">[Release: 12] [Status: Closed/Delivered] [Updated: 2019/02/21]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/344">JEP 344: Abortable Mixed Collections for G1</a><div class="jepstatus">[Release: 12] [Status: Closed/Delivered] [Updated: 2019/07/15]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/346">JEP 346: Promptly Return Unused Committed Memory from G1</a><div class="jepstatus">[Release: 12] [Status: Closed/Delivered] [Updated: 2019/01/23]</div></div>
</div>
</div>
<div class="project" id="jdk/13">
<h2><a href="https://openjdk.java.net/projects/jdk/13">JDK13</a></h2>
<div class="description">JDK 13 is the open-source reference implementation of version 13 of the Java SE Platform as specified by by JSR 388 in the Java Community Process.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/350">JEP 350: Dynamic CDS Archives</a><div class="jepstatus">[Release: 13] [Status: Closed/Delivered] [Updated: 2021/10/14]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/351">JEP 351: ZGC: Uncommit Unused Memory (Experimental)</a><div class="jepstatus">[Release: 13] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/353">JEP 353: Reimplement the Legacy Socket API</a><div class="jepstatus">[Release: 13] [Status: Closed/Delivered] [Updated: 2020/09/14]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/354">JEP 354: Switch Expressions (Second Preview)</a><div class="jepstatus">[Release: 13] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/355">JEP 355: Text Blocks (Preview)</a><div class="jepstatus">[Release: 13] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
</div>
</div>
<div class="project" id="jdk/14">
<h2><a href="https://openjdk.java.net/projects/jdk/14">JDK14</a></h2>
<div class="description">JDK 14 is the open-source reference implementation of version 14 of the Java SE Platform as specified by by JSR 389 in the Java Community Process.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/305">JEP 305: Pattern Matching for instanceof (Preview)</a><div class="jepstatus">[Release: 14] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/343">JEP 343: Packaging Tool (Incubator)</a><div class="jepstatus">[Release: 14] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/345">JEP 345: NUMA-Aware Memory Allocation for G1</a><div class="jepstatus">[Release: 14] [Status: Closed/Delivered] [Updated: 2020/02/27]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/349">JEP 349: JFR Event Streaming</a><div class="jepstatus">[Release: 14] [Status: Closed/Delivered] [Updated: 2020/02/25]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/352">JEP 352: Non-Volatile Mapped Byte Buffers</a><div class="jepstatus">[Release: 14] [Status: Closed/Delivered] [Updated: 2022/08/16]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/358">JEP 358: Helpful NullPointerExceptions</a><div class="jepstatus">[Release: 14] [Status: Closed/Delivered] [Updated: 2021/12/22]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/359">JEP 359: Records (Preview)</a><div class="jepstatus">[Release: 14] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/361">JEP 361: Switch Expressions</a><div class="jepstatus">[Release: 14] [Status: Closed/Delivered] [Updated: 2022/03/11]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/362">JEP 362: Deprecate the Solaris and SPARC Ports</a><div class="jepstatus">[Release: 14] [Status: Closed/Delivered] [Updated: 2021/08/27]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/363">JEP 363: Remove the Concurrent Mark Sweep (CMS) Garbage Collector</a><div class="jepstatus">[Release: 14] [Status: Closed/Delivered] [Updated: 2020/06/18]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/364">JEP 364: ZGC on macOS (Experimental)</a><div class="jepstatus">[Release: 14] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/365">JEP 365: ZGC on Windows (Experimental)</a><div class="jepstatus">[Release: 14] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/366">JEP 366: Deprecate the ParallelScavenge + SerialOld GC Combination</a><div class="jepstatus">[Release: 14] [Status: Closed/Delivered] [Updated: 2020/02/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/367">JEP 367: Remove the Pack200 Tools and API</a><div class="jepstatus">[Release: 14] [Status: Closed/Delivered] [Updated: 2020/02/22]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/368">JEP 368: Text Blocks (Second Preview)</a><div class="jepstatus">[Release: 14] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/370">JEP 370: Foreign-Memory Access API (Incubator)</a><div class="jepstatus">[Release: 14] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
</div>
</div>
<div class="project" id="jdk/15">
<h2><a href="https://openjdk.java.net/projects/jdk/15">JDK15</a></h2>
<div class="description">JDK 15 is the open-source reference implementation of version 15 of the Java SE Platform, as specified by by JSR 390 in the Java Community Process.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/339">JEP 339: Edwards-Curve Digital Signature Algorithm (EdDSA)</a><div class="jepstatus">[Release: 15] [Status: Closed/Delivered] [Updated: 2020/10/13]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/360">JEP 360: Sealed Classes (Preview)</a><div class="jepstatus">[Release: 15] [Status: Closed/Delivered] [Updated: 2022/03/11]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/371">JEP 371: Hidden Classes</a><div class="jepstatus">[Release: 15] [Status: Closed/Delivered] [Updated: 2020/10/07]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/372">JEP 372: Remove the Nashorn JavaScript Engine</a><div class="jepstatus">[Release: 15] [Status: Closed/Delivered] [Updated: 2020/12/15]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/373">JEP 373: Reimplement the Legacy DatagramSocket API</a><div class="jepstatus">[Release: 15] [Status: Closed/Delivered] [Updated: 2023/03/04]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/374">JEP 374: Deprecate and Disable Biased Locking</a><div class="jepstatus">[Release: 15] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/375">JEP 375: Pattern Matching for instanceof (Second Preview)</a><div class="jepstatus">[Release: 15] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/377">JEP 377: ZGC: A Scalable Low-Latency Garbage Collector (Production)</a><div class="jepstatus">[Release: 15] [Status: Closed/Delivered] [Updated: 2023/03/06]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/378">JEP 378: Text Blocks</a><div class="jepstatus">[Release: 15] [Status: Closed/Delivered] [Updated: 2020/07/30]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/379">JEP 379: Shenandoah: A Low-Pause-Time Garbage Collector (Production)</a><div class="jepstatus">[Release: 15] [Status: Closed/Delivered] [Updated: 2021/11/10]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/381">JEP 381: Remove the Solaris and SPARC Ports</a><div class="jepstatus">[Release: 15] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/383">JEP 383: Foreign-Memory Access API (Second Incubator)</a><div class="jepstatus">[Release: 15] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/384">JEP 384: Records (Second Preview)</a><div class="jepstatus">[Release: 15] [Status: Closed/Delivered] [Updated: 2022/03/11]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/385">JEP 385: Deprecate RMI Activation for Removal</a><div class="jepstatus">[Release: 15] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
</div>
</div>
<div class="project" id="jdk/16">
<h2><a href="https://openjdk.java.net/projects/jdk/16">JDK16</a></h2>
<div class="description">JDK 16 is the open-source reference implementation of version 16 of the Java SE Platform, as specified by by JSR 390 in the Java Community Process.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/338">JEP 338: Vector API (Incubator)</a><div class="jepstatus">[Release: 16] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/347">JEP 347: Enable C++14 Language Features</a><div class="jepstatus">[Release: 16] [Status: Closed/Delivered] [Updated: 2021/09/25]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/357">JEP 357: Migrate from Mercurial to Git</a><div class="jepstatus">[Release: 16] [Status: Closed/Delivered] [Updated: 2021/01/27]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/376">JEP 376: ZGC: Concurrent Thread-Stack Processing</a><div class="jepstatus">[Release: 16] [Status: Closed/Delivered] [Updated: 2021/03/07]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/380">JEP 380: Unix-Domain Socket Channels</a><div class="jepstatus">[Release: 16] [Status: Closed/Delivered] [Updated: 2021/06/29]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/386">JEP 386: Alpine Linux Port</a><div class="jepstatus">[Release: 16] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/387">JEP 387: Elastic Metaspace</a><div class="jepstatus">[Release: 16] [Status: Closed/Delivered] [Updated: 2023/02/14]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/388">JEP 388: Windows/AArch64 Port</a><div class="jepstatus">[Release: 16] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/389">JEP 389: Foreign Linker API (Incubator)</a><div class="jepstatus">[Release: 16] [Status: Closed/Delivered] [Updated: 2022/03/02]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/390">JEP 390: Warnings for Value-Based Classes</a><div class="jepstatus">[Release: 16] [Status: Closed/Delivered] [Updated: 2021/08/30]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/392">JEP 392: Packaging Tool</a><div class="jepstatus">[Release: 16] [Status: Closed/Delivered] [Updated: 2021/02/19]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/393">JEP 393: Foreign-Memory Access API (Third Incubator)</a><div class="jepstatus">[Release: 16] [Status: Closed/Delivered] [Updated: 2022/03/02]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/394">JEP 394: Pattern Matching for instanceof</a><div class="jepstatus">[Release: 16] [Status: Closed/Delivered] [Updated: 2022/06/10]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/395">JEP 395: Records</a><div class="jepstatus">[Release: 16] [Status: Closed/Delivered] [Updated: 2022/02/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/396">JEP 396: Strongly Encapsulate JDK Internals by Default</a><div class="jepstatus">[Release: 16] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/397">JEP 397: Sealed Classes (Second Preview)</a><div class="jepstatus">[Release: 16] [Status: Closed/Delivered] [Updated: 2022/03/11]</div></div>
</div>
</div>
<div class="project" id="jdk/17">
<h2><a href="https://openjdk.java.net/projects/jdk/17">JDK17</a></h2>
<div class="description">JDK 17 is the open-source reference implementation of version 17 of the Java SE Platform, as specified by by JSR 390 in the Java Community Process.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/306">JEP 306: Restore Always-Strict Floating-Point Semantics</a><div class="jepstatus">[Release: 17] [Status: Closed/Delivered] [Updated: 2021/08/02]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/356">JEP 356: Enhanced Pseudo-Random Number Generators</a><div class="jepstatus">[Release: 17] [Status: Closed/Delivered] [Updated: 2023/02/01]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/382">JEP 382: New macOS Rendering Pipeline</a><div class="jepstatus">[Release: 17] [Status: Closed/Delivered] [Updated: 2022/04/05]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/391">JEP 391: macOS/AArch64 Port</a><div class="jepstatus">[Release: 17] [Status: Closed/Delivered] [Updated: 2022/11/23]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/398">JEP 398: Deprecate the Applet API for Removal</a><div class="jepstatus">[Release: 17] [Status: Closed/Delivered] [Updated: 2021/08/09]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/403">JEP 403: Strongly Encapsulate JDK Internals</a><div class="jepstatus">[Release: 17] [Status: Closed/Delivered] [Updated: 2021/09/08]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/406">JEP 406: Pattern Matching for switch (Preview)</a><div class="jepstatus">[Release: 17] [Status: Closed/Delivered] [Updated: 2022/06/01]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/407">JEP 407: Remove RMI Activation</a><div class="jepstatus">[Release: 17] [Status: Closed/Delivered] [Updated: 2021/07/07]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/409">JEP 409: Sealed Classes</a><div class="jepstatus">[Release: 17] [Status: Closed/Delivered] [Updated: 2022/03/19]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/410">JEP 410: Remove the Experimental AOT and JIT Compiler</a><div class="jepstatus">[Release: 17] [Status: Closed/Delivered] [Updated: 2021/08/05]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/411">JEP 411: Deprecate the Security Manager for Removal</a><div class="jepstatus">[Release: 17] [Status: Closed/Delivered] [Updated: 2022/07/26]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/412">JEP 412: Foreign Function & Memory API (Incubator)</a><div class="jepstatus">[Release: 17] [Status: Closed/Delivered] [Updated: 2022/03/02]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/414">JEP 414: Vector API (Second Incubator)</a><div class="jepstatus">[Release: 17] [Status: Closed/Delivered] [Updated: 2023/02/27]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/415">JEP 415: Context-Specific Deserialization Filters</a><div class="jepstatus">[Release: 17] [Status: Closed/Delivered] [Updated: 2022/04/08]</div></div>
</div>
</div>
<div class="project" id="jdk/18">
<h2><a href="https://openjdk.java.net/projects/jdk/18">JDK18</a></h2>
<div class="description">JDK 18 is the open-source reference implementation of version 18 of the Java SE Platform, as specified by by JSR 393 in the Java Community Process.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/400">JEP 400: UTF-8 by Default</a><div class="jepstatus">[Release: 18] [Status: Closed/Delivered] [Updated: 2022/10/19]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/408">JEP 408: Simple Web Server</a><div class="jepstatus">[Release: 18] [Status: Closed/Delivered] [Updated: 2022/03/07]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/413">JEP 413: Code Snippets in Java API Documentation</a><div class="jepstatus">[Release: 18] [Status: Closed/Delivered] [Updated: 2022/02/10]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/416">JEP 416: Reimplement Core Reflection with Method Handles</a><div class="jepstatus">[Release: 18] [Status: Closed/Delivered] [Updated: 2022/02/09]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/417">JEP 417: Vector API (Third Incubator)</a><div class="jepstatus">[Release: 18] [Status: Closed/Delivered] [Updated: 2023/02/27]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/418">JEP 418: Internet-Address Resolution SPI</a><div class="jepstatus">[Release: 18] [Status: Closed/Delivered] [Updated: 2022/09/09]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/419">JEP 419: Foreign Function & Memory API (Second Incubator)</a><div class="jepstatus">[Release: 18] [Status: Closed/Delivered] [Updated: 2022/03/02]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/420">JEP 420: Pattern Matching for switch (Second Preview)</a><div class="jepstatus">[Release: 18] [Status: Closed/Delivered] [Updated: 2022/03/22]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/421">JEP 421: Deprecate Finalization for Removal</a><div class="jepstatus">[Release: 18] [Status: Closed/Delivered] [Updated: 2022/09/02]</div></div>
</div>
</div>
<div class="project" id="jdk/19">
<h2><a href="https://openjdk.java.net/projects/jdk/19">JDK19</a></h2>
<div class="description">JDK 19 is the open-source reference implementation of version 19 of the Java SE Platform, as specified by by JSR 394 in the Java Community Process.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/405">JEP 405: Record Patterns (Preview)</a><div class="jepstatus">[Release: 19] [Status: Closed/Delivered] [Updated: 2022/10/06]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/422">JEP 422: Linux/RISC-V Port</a><div class="jepstatus">[Release: 19] [Status: Closed/Delivered] [Updated: 2022/09/08]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/424">JEP 424: Foreign Function & Memory API (Preview)</a><div class="jepstatus">[Release: 19] [Status: Closed/Delivered] [Updated: 2022/12/14]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/425">JEP 425: Virtual Threads (Preview)</a><div class="jepstatus">[Release: 19] [Status: Closed/Delivered] [Updated: 2023/01/18]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/426">JEP 426: Vector API (Fourth Incubator)</a><div class="jepstatus">[Release: 19] [Status: Closed/Delivered] [Updated: 2023/03/01]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/427">JEP 427: Pattern Matching for switch (Third Preview)</a><div class="jepstatus">[Release: 19] [Status: Closed/Delivered] [Updated: 2022/10/06]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/428">JEP 428: Structured Concurrency (Incubator)</a><div class="jepstatus">[Release: 19] [Status: Closed/Delivered] [Updated: 2022/08/10]</div></div>
</div>
</div>
<div class="project" id="jdk/20">
<h2><a href="https://openjdk.java.net/projects/jdk/20">JDK20</a></h2>
<div class="description">This release is the Reference Implementation of version 20 of the Java SE Platform, as specified by JSR 395 in the Java Community Process.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/429">JEP 429: Scoped Values (Incubator)</a><div class="jepstatus">[Release: 20] [Status: Closed/Delivered] [Updated: 2023/02/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/432">JEP 432: Record Patterns (Second Preview)</a><div class="jepstatus">[Release: 20] [Status: Closed/Delivered] [Updated: 2023/03/02]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/433">JEP 433: Pattern Matching for switch (Fourth Preview)</a><div class="jepstatus">[Release: 20] [Status: Closed/Delivered] [Updated: 2023/03/21]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/434">JEP 434: Foreign Function & Memory API (Second Preview)</a><div class="jepstatus">[Release: 20] [Status: Closed/Delivered] [Updated: 2023/03/25]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/436">JEP 436: Virtual Threads (Second Preview)</a><div class="jepstatus">[Release: 20] [Status: Closed/Delivered] [Updated: 2023/03/02]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/437">JEP 437: Structured Concurrency (Second Incubator)</a><div class="jepstatus">[Release: 20] [Status: Closed/Delivered] [Updated: 2023/03/02]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/438">JEP 438: Vector API (Fifth Incubator)</a><div class="jepstatus">[Release: 20] [Status: Closed/Delivered] [Updated: 2023/03/13]</div></div>
</div>
</div>
<div class="project" id="jigsaw">
<h2><a href="https://openjdk.java.net/projects/jigsaw">Jigsaw</a></h2>
<div class="description">The primary goals of this Project were to:<ul><li> Make it easier for developers to construct and maintain libraries and large applications; </li>
<li> Improve the security and maintainability of Java&nbsp;SE Platform Implementations in general, and the JDK in particular; </li>
<li> Enable improved application performance; and </li>
<li> Enable the Java SE Platform, and the JDK, to scale down for use in small computing devices and dense cloud deployments. </li></ul></div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/161">JEP 161: Compact Profiles</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2023/01/26]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/162">JEP 162: Prepare for Modularization</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2017/06/14]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/200">JEP 200: The Modular JDK</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/09/21]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/201">JEP 201: Modular Source Code</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2020/12/07]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/220">JEP 220: Modular Run-Time Images</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/09/22]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/238">JEP 238: Multi-Release JAR Files</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/06/22]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/253">JEP 253: Prepare JavaFX UI Controls & CSS APIs for Modularization</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/03/10]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/260">JEP 260: Encapsulate Most Internal APIs</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/09/25]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/261">JEP 261: Module System</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/09/22]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/275">JEP 275: Modular Java Application Packaging</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/04/27]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/282">JEP 282: jlink: The Java Linker</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/05/19]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/293">JEP 293: Guidelines for JDK Command-Line Tool Options</a><div class="jepstatus">[Status: Candidate] [Updated: 2016/07/11]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/396">JEP 396: Strongly Encapsulate JDK Internals by Default</a><div class="jepstatus">[Release: 16] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/403">JEP 403: Strongly Encapsulate JDK Internals</a><div class="jepstatus">[Release: 17] [Status: Closed/Delivered] [Updated: 2021/09/08]</div></div>
</div>
</div>
<div class="project" id="kulla">
<h2><a href="https://openjdk.java.net/projects/kulla">Kulla</a></h2>
<div class="description">The goal of this Project is to investigate the creation of a Read Evaluate Print Loop (REPL) tool for the Java programming language as described in the corresponding JEP.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/222">JEP 222: jshell: The Java Shell (Read-Eval-Print Loop)</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/06/09]</div></div>
</div>
</div>
<div class="project" id="lambda">
<h2><a href="https://openjdk.java.net/projects/lambda">Lambda</a></h2>
<div class="description">JSR 335 (Lambda Expressions for the Java Programming Language) supports programming in a multicore environment by adding closures and related features to the Java language. The JSR has reached its Final Release; these changes to the platform are part of the umbrella JSR 337 and have been integrated into Java SE 8 (modifying the language, JVM, and library specifications).</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/101">JEP 101: Generalized Target-Type Inference</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2015/02/26]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/107">JEP 107: Bulk Data Operations for Collections</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2014/07/10]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/126">JEP 126: Lambda Expressions & Virtual Extension Methods</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2015/01/09]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/186">JEP 186: Collection Literals</a><div class="jepstatus">[Status: Closed/Withdrawn] [Updated: 2022/06/28]</div></div>
</div>
</div>
<div class="project" id="lanai">
<h2><a href="https://openjdk.java.net/projects/lanai">Lanai</a></h2>
<div class="description">The goal of this Project is to implement a new graphics rendering pipeline for macOS.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/382">JEP 382: New macOS Rendering Pipeline</a><div class="jepstatus">[Release: 17] [Status: Closed/Delivered] [Updated: 2022/04/05]</div></div>
</div>
</div>
<div class="project" id="locale-enhancement">
<h2><a href="https://openjdk.java.net/projects/locale-enhancement">Locale Enhancement</a></h2>
<div class="description">The goal of this Project is to enhance the java.util.Locale class in order to bring the Java platform into conformance with IETF BCP47 and UTR35(CLDR/LDML). A detailed proposal may be found here.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/314">JEP 314: Additional Unicode Language-Tag Extensions</a><div class="jepstatus">[Release: 10] [Status: Closed/Delivered] [Updated: 2018/03/06]</div></div>
</div>
</div>
<div class="project" id="loom">
<h2><a href="https://openjdk.java.net/projects/loom">Loom</a></h2>
<div class="description">PLEASE NOTE! Go to the Wiki for additional and up-to-date information. (<a href="https://wiki.openjdk.java.net/display/loom">https://wiki.openjdk.java.net/display/loom</a>)</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/353">JEP 353: Reimplement the Legacy Socket API</a><div class="jepstatus">[Release: 13] [Status: Closed/Delivered] [Updated: 2020/09/14]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/373">JEP 373: Reimplement the Legacy DatagramSocket API</a><div class="jepstatus">[Release: 15] [Status: Closed/Delivered] [Updated: 2023/03/04]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/416">JEP 416: Reimplement Core Reflection with Method Handles</a><div class="jepstatus">[Release: 18] [Status: Closed/Delivered] [Updated: 2022/02/09]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/425">JEP 425: Virtual Threads (Preview)</a><div class="jepstatus">[Release: 19] [Status: Closed/Delivered] [Updated: 2023/01/18]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/428">JEP 428: Structured Concurrency (Incubator)</a><div class="jepstatus">[Release: 19] [Status: Closed/Delivered] [Updated: 2022/08/10]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/429">JEP 429: Scoped Values (Incubator)</a><div class="jepstatus">[Release: 20] [Status: Closed/Delivered] [Updated: 2023/02/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/436">JEP 436: Virtual Threads (Second Preview)</a><div class="jepstatus">[Release: 20] [Status: Closed/Delivered] [Updated: 2023/03/02]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/437">JEP 437: Structured Concurrency (Second Incubator)</a><div class="jepstatus">[Release: 20] [Status: Closed/Delivered] [Updated: 2023/03/02]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/444">JEP 444: Virtual Threads</a><div class="jepstatus">[Release: 21] [Status: Proposed to Target] [Updated: 2023/03/31]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/8304357">JEP 8304357: JEP draft: Scoped Values (Preview)</a><div class="jepstatus">[Status: Submitted] [Updated: 2023/03/30]</div></div>
</div>
</div>
<div class="project" id="jmm">
<h2><a href="https://openjdk.java.net/projects/jmm">Memory Model Update</a></h2>
<div class="description">The goal of this Project is to update the Java Memory Model, as described in JEP 188.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/188">JEP 188: Java Memory Model Update</a><div class="jepstatus">[Status: Draft] [Updated: 2016/06/07]</div></div>
</div>
</div>
<div class="project" id="mlvm">
<h2><a href="https://openjdk.java.net/projects/mlvm">Multi-Language VM</a></h2>
<div class="description">We are extending the JVM with first-class architectural support for languages other than Java, especially dynamic languages. This project will prototype a number of extensions to the JVM, so that it can run non-Java languages efficiently, with a performance level comparable to that of Java itself.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/160">JEP 160: Lambda-Form Representation for Method Handles</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2017/10/17]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/169">JEP 169: Larval State for Value Objects</a><div class="jepstatus">[Status: Draft] [Updated: 2021/12/09]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/274">JEP 274: Enhanced Method Handles</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/05/17]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/8158765">JEP 8158765: JEP draft: Isolated Methods</a><div class="jepstatus">[Status: Draft] [Updated: 2018/04/16]</div></div>
</div>
</div>
<div class="project" id="nashorn">
<h2><a href="https://openjdk.java.net/projects/nashorn">Nashorn</a></h2>
<div class="description">Nashorn's goal is to implement a lightweight high-performance JavaScript runtime in Java with a native JVM. This Project intends to enable Java developers embedding of JavaScript in Java applications via JSR-223 and to develop free standing JavaScript applications using the jrunscript command-line tool.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/174">JEP 174: Nashorn JavaScript Engine</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2015/02/13]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/194">JEP 194: Nashorn Code Persistence</a><div class="jepstatus">[Status: Closed/Withdrawn] [Updated: 2015/01/06]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/196">JEP 196: Nashorn Optimistic Typing</a><div class="jepstatus">[Release: 8u40] [Status: Closed/Delivered] [Updated: 2014/12/05]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/202">JEP 202: Nashorn Class Filter</a><div class="jepstatus">[Release: 8u40] [Status: Closed/Delivered] [Updated: 2017/05/17]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/203">JEP 203: Nashorn: Lexically-Scoped Variable & Constant Declarations</a><div class="jepstatus">[Release: 8u40] [Status: Closed/Delivered] [Updated: 2017/05/17]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/236">JEP 236: Parser API for Nashorn</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/05/17]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/292">JEP 292: Implement Selected ECMAScript 6 Features in Nashorn</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/03/09]</div></div>
</div>
</div>
<div class="project" id="nio">
<h2><a href="https://openjdk.java.net/projects/nio">New I/O</a></h2>
<div class="description">This Project's mission is to produce the implementation of the (New) New I/O APIs being defined by JSR 203 as well as related work in the JDK.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/337">JEP 337: RDMA Network Sockets</a><div class="jepstatus">[Status: Candidate] [Updated: 2020/02/12]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/380">JEP 380: Unix-Domain Socket Channels</a><div class="jepstatus">[Release: 16] [Status: Closed/Delivered] [Updated: 2021/06/29]</div></div>
</div>
</div>
<div class="project" id="openjfx">
<h2><a href="https://openjdk.java.net/projects/openjfx">OpenJFX</a></h2>
<div class="description">OpenJFX is the open source home of JavaFX development. The goal of OpenJFX is to build the next-generation Java client toolkit.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/204">JEP 204: JavaFX Accessibility</a><div class="jepstatus">[Release: 8u40] [Status: Closed/Delivered] [Updated: 2016/06/07]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/205">JEP 205: New Controls for JavaFX</a><div class="jepstatus">[Release: 8u40] [Status: Closed/Delivered] [Updated: 2015/03/03]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/206">JEP 206: Modernize the JavaFX Media Stack on Mac OS X</a><div class="jepstatus">[Release: 8u40] [Status: Closed/Delivered] [Updated: 2015/02/26]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/208">JEP 208: Java Packager Improvements</a><div class="jepstatus">[Release: 8u40] [Status: Closed/Delivered] [Updated: 2015/02/26]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/209">JEP 209: JavaFX Scene Builder Update</a><div class="jepstatus">[Release: 8u40] [Status: Closed/Delivered] [Updated: 2014/10/01]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/239">JEP 239: Update JavaFX/WebView to Newer Version of WebKit</a><div class="jepstatus">[Release: 8u60] [Status: Closed/Delivered] [Updated: 2016/08/24]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/253">JEP 253: Prepare JavaFX UI Controls & CSS APIs for Modularization</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/03/10]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/257">JEP 257: Update JavaFX/Media to Newer Version of GStreamer</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2022/11/21]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/275">JEP 275: Modular Java Application Packaging</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/04/27]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/283">JEP 283: Enable GTK 3 on Linux</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2018/10/12]</div></div>
</div>
</div>
<div class="project" id="panama">
<h2><a href="https://openjdk.java.net/projects/panama">Panama</a></h2>
<div class="description">We are improving and enriching the connections between the Java virtual machine and well-defined but “foreign” (non-Java) APIs, including many interfaces commonly used by C programmers.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/338">JEP 338: Vector API (Incubator)</a><div class="jepstatus">[Release: 16] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/370">JEP 370: Foreign-Memory Access API (Incubator)</a><div class="jepstatus">[Release: 14] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/380">JEP 380: Unix-Domain Socket Channels</a><div class="jepstatus">[Release: 16] [Status: Closed/Delivered] [Updated: 2021/06/29]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/383">JEP 383: Foreign-Memory Access API (Second Incubator)</a><div class="jepstatus">[Release: 15] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/389">JEP 389: Foreign Linker API (Incubator)</a><div class="jepstatus">[Release: 16] [Status: Closed/Delivered] [Updated: 2022/03/02]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/393">JEP 393: Foreign-Memory Access API (Third Incubator)</a><div class="jepstatus">[Release: 16] [Status: Closed/Delivered] [Updated: 2022/03/02]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/412">JEP 412: Foreign Function & Memory API (Incubator)</a><div class="jepstatus">[Release: 17] [Status: Closed/Delivered] [Updated: 2022/03/02]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/414">JEP 414: Vector API (Second Incubator)</a><div class="jepstatus">[Release: 17] [Status: Closed/Delivered] [Updated: 2023/02/27]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/417">JEP 417: Vector API (Third Incubator)</a><div class="jepstatus">[Release: 18] [Status: Closed/Delivered] [Updated: 2023/02/27]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/419">JEP 419: Foreign Function & Memory API (Second Incubator)</a><div class="jepstatus">[Release: 18] [Status: Closed/Delivered] [Updated: 2022/03/02]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/424">JEP 424: Foreign Function & Memory API (Preview)</a><div class="jepstatus">[Release: 19] [Status: Closed/Delivered] [Updated: 2022/12/14]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/426">JEP 426: Vector API (Fourth Incubator)</a><div class="jepstatus">[Release: 19] [Status: Closed/Delivered] [Updated: 2023/03/01]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/434">JEP 434: Foreign Function & Memory API (Second Preview)</a><div class="jepstatus">[Release: 20] [Status: Closed/Delivered] [Updated: 2023/03/25]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/438">JEP 438: Vector API (Fifth Incubator)</a><div class="jepstatus">[Release: 20] [Status: Closed/Delivered] [Updated: 2023/03/13]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/442">JEP 442: Foreign Function & Memory API (Third Preview)</a><div class="jepstatus">[Status: Proposed to Target] [Updated: 2023/04/03]</div></div>
</div>
</div>
<div class="project" id="aarch32-port">
<h2><a href="https://openjdk.java.net/projects/aarch32-port">Port: AArch32</a></h2>
<div class="description">The goal of this Project is to provide a full featured port of OpenJDK on the Linux/AArch32 platoform. AArch32 is the 32-bit sub-architecture within the ARMv8 architecture. The port will be fully compatible with ARMv7 and may support ARMv6 depending on community interest.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/297">JEP 297: Unified arm32/arm64 Port</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2022/08/11]</div></div>
</div>
</div>
<div class="project" id="aarch64-port">
<h2><a href="https://openjdk.java.net/projects/aarch64-port">Port: AArch64</a></h2>
<div class="description">The goal of this Project is to provide a full-featured and certified version of OpenJDK on the Linux/AArch64 platform which can be integrated into JDK 8. AArch64 is the 64-bit mode of ARMv8; it is a completely new architecture, and is not compatible with the 32-bit ARM instruction set. It is hoped that this project will eventually be able to support operating systems other than GNU/Linux, and welcomes contributors with the necessary expertise.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/237">JEP 237: Linux/AArch64 Port</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2017/03/08]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/388">JEP 388: Windows/AArch64 Port</a><div class="jepstatus">[Release: 16] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/391">JEP 391: macOS/AArch64 Port</a><div class="jepstatus">[Release: 17] [Status: Closed/Delivered] [Updated: 2022/11/23]</div></div>
</div>
</div>
<div class="project" id="ppc-aix-port">
<h2><a href="https://openjdk.java.net/projects/ppc-aix-port">Port: PowerPC/AIX</a></h2>
<div class="description">The goal of this project is to provide a full-featured and certifiable version of OpenJDK on the Linux/PowerPC and AIX/PowerPC platforms which can be ultimately integrated into the main OpenJDK development branches.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/175">JEP 175: PowerPC/AIX Port</a><div class="jepstatus">[Release: 8u20] [Status: Closed/Delivered] [Updated: 2017/08/17]</div></div>
</div>
</div>
<div class="project" id="riscv-port">
<h2><a href="https://openjdk.java.net/projects/riscv-port">Port: RISC-V</a></h2>
<div class="description">The goal of this Project is to deliver a full-featured port of OpenJDK on the Linux/RISC-V platform which may be integrated into the main OpenJDK development branch.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/422">JEP 422: Linux/RISC-V Port</a><div class="jepstatus">[Release: 19] [Status: Closed/Delivered] [Updated: 2022/09/08]</div></div>
</div>
</div>
<div class="project" id="s390x-port">
<h2><a href="https://openjdk.java.net/projects/s390x-port">Port: s390x</a></h2>
<div class="description">The goal of this Project is to integrate SAP's full-featured and certifiable Linux/s390x port of the OpenJDK into the main OpenJDK development branch.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/294">JEP 294: Linux/s390x Port</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2019/08/13]</div></div>
</div>
</div>
<div class="project" id="portola">
<h2><a href="https://openjdk.java.net/projects/portola">Portola</a></h2>
<div class="description">The goal of this Project is to provide a port of the JDK to the Alpine Linux distribution, and in particular the musl C library.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/386">JEP 386: Alpine Linux Port</a><div class="jepstatus">[Release: 16] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
</div>
</div>
<div class="project" id="shenandoah">
<h2><a href="https://openjdk.java.net/projects/shenandoah">Shenandoah</a></h2>
<div class="description">Shenandoah is an ultra-low pause time garbage collector that reduces GC pause times by performing more garbage collection work concurrently with the running Java program. CMS and G1 both perform concurrent marking of live objects. Shenandoah adds concurrent compaction.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/189">JEP 189: Shenandoah: A Low-Pause-Time Garbage Collector (Experimental)</a><div class="jepstatus">[Release: 12] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/404">JEP 404: Generational Shenandoah</a><div class="jepstatus">[Status: Candidate] [Updated: 2023/01/18]</div></div>
</div>
</div>
<div class="project" id="skara">
<h2><a href="https://openjdk.java.net/projects/skara">Skara</a></h2>
<div class="description">The goal of this Project is to investigate alternative SCM and code review options for the JDK source code, including options based upon Git rather than Mercurial, and including options hosted by third parties.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/357">JEP 357: Migrate from Mercurial to Git</a><div class="jepstatus">[Release: 16] [Status: Closed/Delivered] [Updated: 2021/01/27]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/369">JEP 369: Migrate to GitHub</a><div class="jepstatus">[Release: 16] [Status: Closed/Delivered] [Updated: 2021/01/15]</div></div>
</div>
</div>
<div class="project" id="tiered-attrib">
<h2><a href="https://openjdk.java.net/projects/tiered-attrib">Tiered Attribution</a></h2>
<div class="description">The goal of this Project is to investigate an alternate architecture for the javac type-checking subsystem which is free from speculative attribution. Additional details may be found in JEP 215: Tiered Attribution for javac.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/215">JEP 215: Tiered Attribution for javac</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2016/07/12]</div></div>
</div>
</div>
<div class="project" id="type-annotations">
<h2><a href="https://openjdk.java.net/projects/type-annotations">Type Annotations</a></h2>
<div class="description">The goals of the Type Annotations Project were:<ul><li> Bugs in how <tt>javac</tt> handles type annotations are tracked in <a href="https://bugs.openjdk.org/issues/?jql=project%20%3D%2010100%20AND%20component%20%3D%2010308%20AND%20cf[10008]%20%3D%20212"> tools/javac</a>. </li>
<li> Bugs in how Core Reflection exposes type annotations are tracked in <a href="https://bugs.openjdk.org/issues/?jql=project%20%3D%2010100%20AND%20component%20%3D%2010300%20AND%20cf[10008]%20%3D%20668"> core-libs/java.lang:reflect</a>. </li>
<li> Bugs in how Annotation Processing exposes type annotations are tracked in <a href="https://bugs.openjdk.org/issues/?jql=project%20%3D%2010100%20AND%20component%20%3D%2010300%20AND%20cf[10008]%20%3D%20233"> core-libs/javax.lang.model</a>. </li></ul></div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/104">JEP 104: Type Annotations</a><div class="jepstatus">[Release: 8] [Status: Closed/Delivered] [Updated: 2020/06/01]</div></div>
</div>
</div>
<div class="project" id="valhalla">
<h2><a href="https://openjdk.java.net/projects/valhalla">Valhalla</a></h2>
<div class="description">Project Valhalla is augmenting the Java object model with value objects and user-defined primitives, combining the abstractions of object-oriented programming with the performance characteristics of simple primitives. These features will be complemented with changes to Javas generics to preserve performance gains through generic APIs.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/181">JEP 181: Nest-Based Access Control</a><div class="jepstatus">[Release: 11] [Status: Closed/Delivered] [Updated: 2021/04/24]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/218">JEP 218: Generics over Primitive Types</a><div class="jepstatus">[Status: Candidate] [Updated: 2017/10/17]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/303">JEP 303: Intrinsics for the LDC and INVOKEDYNAMIC Instructions</a><div class="jepstatus">[Status: Candidate] [Updated: 2018/09/11]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/309">JEP 309: Dynamic Class-File Constants</a><div class="jepstatus">[Release: 11] [Status: Closed/Delivered] [Updated: 2018/09/10]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/334">JEP 334: JVM Constants API</a><div class="jepstatus">[Release: 12] [Status: Closed/Delivered] [Updated: 2022/08/02]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/338">JEP 338: Vector API (Incubator)</a><div class="jepstatus">[Release: 16] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/371">JEP 371: Hidden Classes</a><div class="jepstatus">[Release: 15] [Status: Closed/Delivered] [Updated: 2020/10/07]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/390">JEP 390: Warnings for Value-Based Classes</a><div class="jepstatus">[Release: 16] [Status: Closed/Delivered] [Updated: 2021/08/30]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/401">JEP 401: Implicitly-Initialized Value Object Storage (Preview)</a><div class="jepstatus">[Status: Draft] [Updated: 2023/03/29]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/402">JEP 402: Enhanced Primitive Boxing (Preview)</a><div class="jepstatus">[Status: Draft] [Updated: 2023/03/21]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/414">JEP 414: Vector API (Second Incubator)</a><div class="jepstatus">[Release: 17] [Status: Closed/Delivered] [Updated: 2023/02/27]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/416">JEP 416: Reimplement Core Reflection with Method Handles</a><div class="jepstatus">[Release: 18] [Status: Closed/Delivered] [Updated: 2022/02/09]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/417">JEP 417: Vector API (Third Incubator)</a><div class="jepstatus">[Release: 18] [Status: Closed/Delivered] [Updated: 2023/02/27]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/426">JEP 426: Vector API (Fourth Incubator)</a><div class="jepstatus">[Release: 19] [Status: Closed/Delivered] [Updated: 2023/03/01]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/8261529">JEP 8261529: JEP draft: Universal Generics (Preview)</a><div class="jepstatus">[Status: Draft] [Updated: 2023/03/23]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/8267650">JEP 8267650: JEP draft: Better-defined JVM class file validation</a><div class="jepstatus">[Status: Draft] [Updated: 2022/09/08]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/8277163">JEP 8277163: JEP draft: Value Objects (Preview)</a><div class="jepstatus">[Status: Submitted] [Updated: 2023/03/21]</div></div>
</div>
</div>
<div class="project" id="verona">
<h2><a href="https://openjdk.java.net/projects/verona">Verona</a></h2>
<div class="description">The goal of this Project was to implement the new JDK version string as described in JEP-223. The new version-string scheme was designed to easily distinguish major, minor, and security-update releases.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/223">JEP 223: New Version-String Scheme</a><div class="jepstatus">[Release: 9] [Status: Closed/Delivered] [Updated: 2021/10/03]</div></div>
</div>
</div>
<div class="project" id="zgc">
<h2><a href="https://openjdk.java.net/projects/zgc">ZGC</a></h2>
<div class="description">ZGC is a scalable low-latency garbage collector capable of handling heaps ranging from 8MB to 16TB in size, with sub-millisecond max pause times.</div>
<h3>JEPs</h3>
<div class="jeps">
<div class="jep"><a href="https://openjdk.java.net/jeps/333">JEP 333: ZGC: A Scalable Low-Latency Garbage Collector (Experimental)</a><div class="jepstatus">[Release: 11] [Status: Closed/Delivered] [Updated: 2020/03/13]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/351">JEP 351: ZGC: Uncommit Unused Memory (Experimental)</a><div class="jepstatus">[Release: 13] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/364">JEP 364: ZGC on macOS (Experimental)</a><div class="jepstatus">[Release: 14] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/365">JEP 365: ZGC on Windows (Experimental)</a><div class="jepstatus">[Release: 14] [Status: Closed/Delivered] [Updated: 2021/08/28]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/376">JEP 376: ZGC: Concurrent Thread-Stack Processing</a><div class="jepstatus">[Release: 16] [Status: Closed/Delivered] [Updated: 2021/03/07]</div></div>
<div class="jep"><a href="https://openjdk.java.net/jeps/377">JEP 377: ZGC: A Scalable Low-Latency Garbage Collector (Production)</a><div class="jepstatus">[Release: 15] [Status: Closed/Delivered] [Updated: 2023/03/06]</div></div>
</div>
</div>
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@ -35,7 +35,9 @@ dependencies {
implementation project(':code:features-index:index-forward')
implementation project(':code:features-index:index-reverse')
implementation project(':code:features-index:lexicon')
implementation project(':code:features-index:domain-ranking')
implementation project(':code:features-search:result-ranking')
implementation libs.lombok

View File

@ -123,8 +123,11 @@ public class SearchIndex {
queryHeads.add(indexReader.findPriorityWord(wordId));
}
// Finally consider terms in the full index
queryHeads.add(indexReader.findFullWord(orderedIncludes[0], ReverseIndexEntrySourceBehavior.DO_NOT_PREFER));
// Finally consider terms in the full index, but only do this for sufficiently long queries
// as short queries tend to be too underspecified to produce anything other than CPU warmth
if (orderedIncludes.length > 3) {
queryHeads.add(indexReader.findFullWord(orderedIncludes[0], ReverseIndexEntrySourceBehavior.DO_NOT_PREFER));
}
for (var query : queryHeads) {
if (query == null) {
@ -178,4 +181,7 @@ public class SearchIndex {
public int getTermFrequency(int id) {
return (int) indexReader.numHits(id);
}
public int getTermFrequencyPrio(int id) {
return (int) indexReader.numHitsPrio(id);
}
}

View File

@ -48,6 +48,9 @@ public class SearchIndexReader {
public long numHits(int word) {
return reverseIndexFullReader.numDocuments(word);
}
public long numHitsPrio(int word) {
return reverseIndexPriorityReader.numDocuments(word);
}
public long[] getMetadata(int wordId, long[] docIds) {
return reverseIndexFullReader.getTermMeta(wordId, docIds);

View File

@ -22,4 +22,8 @@ public record SearchIndexSearchTerms(IntList includes, IntList excludes, IntList
list.sort(comparator);
return list.toIntArray();
}
public int size() {
return includes.size() + excludes.size() + priority.size();
}
}

View File

@ -8,6 +8,7 @@ import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap;
import nu.marginalia.index.client.model.query.SearchSubquery;
import nu.marginalia.index.index.SearchIndex;
import nu.marginalia.index.svc.SearchTermsService;
import nu.marginalia.ranking.ResultValuator;
import java.util.List;
import java.util.OptionalInt;
@ -16,10 +17,15 @@ public class IndexMetadataService {
private final SearchIndex index;
private final SearchTermsService searchTermsService;
private final ResultValuator searchResultValuator;
@Inject
public IndexMetadataService(SearchIndex index, SearchTermsService searchTermsService) {
public IndexMetadataService(SearchIndex index,
SearchTermsService searchTermsService,
ResultValuator searchResultValuator) {
this.index = index;
this.searchTermsService = searchTermsService;
this.searchResultValuator = searchResultValuator;
}
public long getDocumentMetadata(long urlId) {
@ -95,6 +101,10 @@ public class IndexMetadataService {
}
public ResultValuator getSearchResultValuator() {
return searchResultValuator;
}
public static class TermMetadata {
private final Long2LongOpenHashMap termdocToMeta;

View File

@ -3,6 +3,7 @@ package nu.marginalia.index.results;
import gnu.trove.list.TLongList;
import gnu.trove.set.hash.TLongHashSet;
import nu.marginalia.index.client.model.results.SearchResultPreliminaryScore;
import nu.marginalia.index.client.model.results.ResultRankingContext;
import nu.marginalia.model.idx.WordFlags;
import nu.marginalia.model.idx.WordMetadata;
import nu.marginalia.index.query.limit.QueryStrategy;
@ -10,6 +11,7 @@ import nu.marginalia.index.client.model.results.SearchResultItem;
import nu.marginalia.index.client.model.results.SearchResultKeywordScore;
import nu.marginalia.index.client.model.query.SearchSubquery;
import nu.marginalia.index.query.IndexQueryParams;
import nu.marginalia.ranking.ResultValuator;
import java.util.List;
@ -22,11 +24,17 @@ public class IndexResultValuator {
private final IndexMetadataService.TermMetadata termMetadata;
private final IndexMetadataService.QuerySearchTerms searchTerms;
private final ResultRankingContext rankingContext;
private final ResultValuator searchResultValuator;
public IndexResultValuator(IndexMetadataService metadataService,
TLongList results,
ResultRankingContext rankingContext,
List<SearchSubquery> subqueries,
IndexQueryParams queryParams
) {
this.rankingContext = rankingContext;
this.searchResultValuator = metadataService.getSearchResultValuator();
final long[] resultsArray = results.toArray();
@ -40,8 +48,8 @@ public class IndexResultValuator {
resultsWithPriorityTerms = metadataService.getResultsWithPriorityTerms(subqueries, resultsArray);
}
private final int flagsFilterMask =
WordFlags.Title.asBit() | WordFlags.TfIdfHigh.asBit() | WordFlags.UrlDomain.asBit() | WordFlags.UrlPath.asBit();
private final long flagsFilterMask =
WordFlags.Title.asBit() | WordFlags.Subjects.asBit() | WordFlags.UrlDomain.asBit() | WordFlags.UrlPath.asBit();
public SearchResultItem calculatePreliminaryScore(long id) {
@ -52,11 +60,9 @@ public class IndexResultValuator {
long docMetadata = metadataService.getDocumentMetadata(urlIdInt);
int maxPosCount = 0;
int maxBitMask = 0;
int maxFlagsCount = 0;
boolean hasSingleTermMatch = false;
boolean anyAllSynthetic = false;
int maxPositionsSet = 0;
for (int querySetId = 0; querySetId < searchTermVariants.size(); querySetId++) {
@ -65,6 +71,7 @@ public class IndexResultValuator {
SearchResultKeywordScore[] termScoresForSet = new SearchResultKeywordScore[termList.size()];
boolean synthetic = true;
for (int termIdx = 0; termIdx < termList.size(); termIdx++) {
String searchTerm = termList.get(termIdx);
@ -93,40 +100,29 @@ public class IndexResultValuator {
}
int minFlagsCount = 8;
int minPosCount = 1000;
int cominedBitMask = ~0;
int minPositionsSet = 4;
for (var termScore : termScoresForSet) {
final int positionCount = Integer.bitCount(termScore.positions());
final int flagCount = Long.bitCount(termScore.encodedWordMetadata() & flagsFilterMask);
minPosCount = Math.min(minPosCount, positionCount);
minFlagsCount = Math.min(minFlagsCount, flagCount);
cominedBitMask &= termScore.positions();
minPositionsSet = Math.min(minPositionsSet, termScore.positionCount());
}
final int combinedBitmaskBitCount = Integer.bitCount(cominedBitMask);
// Calculate the highest value (overall) of the lowest value (per set) of these search result importance measures
maxBitMask = Math.max(maxBitMask, combinedBitmaskBitCount);
maxPosCount = Math.max(maxPosCount, minPosCount);
maxFlagsCount = Math.max(maxFlagsCount, minFlagsCount);
maxPositionsSet = Math.max(maxPositionsSet, minPositionsSet);
anyAllSynthetic |= synthetic;
hasSingleTermMatch |= (termScoresForSet.length == 1 && minPosCount != 0);
}
final boolean hasPriorityTerm = resultsWithPriorityTerms.contains(id);
double score = searchResultValuator.calculateSearchResultValue(searchResult.keywordScores, 5000, rankingContext);
searchResult.setScore(new SearchResultPreliminaryScore(
docMetadata,
hasSingleTermMatch,
hasPriorityTerm,
anyAllSynthetic,
maxFlagsCount,
Math.min(4, maxPosCount),
Math.min(4, maxBitMask),
anyAllSynthetic
maxPositionsSet,
hasPriorityTerm,
score
));
return searchResult;

View File

@ -9,8 +9,9 @@ import io.prometheus.client.Counter;
import io.prometheus.client.Gauge;
import io.prometheus.client.Histogram;
import nu.marginalia.index.client.model.query.SearchSubquery;
import nu.marginalia.index.client.model.results.ResultRankingParameters;
import nu.marginalia.index.client.model.results.SearchResultItem;
import nu.marginalia.index.client.model.results.SearchResultRankingContext;
import nu.marginalia.index.client.model.results.ResultRankingContext;
import nu.marginalia.index.client.model.results.SearchResultSet;
import nu.marginalia.index.client.model.query.SearchSpecification;
import nu.marginalia.array.buffer.LongQueryBuffer;
@ -33,6 +34,7 @@ import spark.Response;
import spark.Spark;
import java.util.*;
import java.util.stream.Collectors;
@Singleton
public class IndexQueryService {
@ -117,29 +119,39 @@ public class IndexQueryService {
private SearchResultSet executeSearch(SearchParameters params) {
var rankingContext = createRankingContext(params.rankingParams, params.subqueries);
logger.info(queryMarker, "{}", params.queryParams);
var resultIds = evaluateSubqueries(params);
var resultItems = calculateResultScores(params, resultIds);
var resultItems = calculateResultScores(params, rankingContext, resultIds);
logger.info(queryMarker, "After filtering: {} -> {}", resultIds.size(), resultItems.size());
var bestResults = selectBestResults(params, resultItems);
return new SearchResultSet(bestResults, createRankingContext(params.subqueries));
return new SearchResultSet(bestResults, rankingContext);
}
/* This information is routed back up the search service in order to calculate BM-25
* accurately */
private SearchResultRankingContext createRankingContext(List<SearchSubquery> subqueries) {
/* This is used in result ranking, and is also routed back up the search service in order to recalculate BM-25
* accurately */
private ResultRankingContext createRankingContext(ResultRankingParameters rankingParams, List<SearchSubquery> subqueries) {
final var termToId = searchTermsSvc.getAllIncludeTerms(subqueries);
final var termFrequencies = new HashMap<>(termToId);
final var prioFrequencies = new HashMap<>(termToId);
termToId.forEach((key, id) -> termFrequencies.put(key, index.getTermFrequency(id)));
termToId.forEach((key, id) -> prioFrequencies.put(key, index.getTermFrequencyPrio(id)));
return new SearchResultRankingContext(index.getTotalDocCount(), termFrequencies);
return new ResultRankingContext(index.getTotalDocCount(),
rankingParams,
termFrequencies,
prioFrequencies);
}
private TLongList evaluateSubqueries(SearchParameters params) {
final TLongList results = new TLongArrayList(params.fetchSize);
logger.info(queryMarker, "{}", params.queryParams);
outer:
// These queries are various term combinations
for (var subquery : params.subqueries) {
@ -151,31 +163,31 @@ public class IndexQueryService {
logSearchTerms(subquery, searchTerms);
int subqueryCount = 0;
// These queries are different indices for one subquery
List<IndexQuery> queries = params.createIndexQueries(index, searchTerms);
for (var query : queries) {
var resultsForSq = executeQuery(query, params);
var resultsForSq = executeQuery(query, params, fetchSizeMultiplier(params, searchTerms));
logger.info(queryMarker, "{} from {}", resultsForSq.size(), query);
results.addAll(resultsForSq);
subqueryCount += resultsForSq.size();
if (!params.hasTimeLeft()) {
logger.info("Query timed out {}, ({}), -{}",
subquery.searchTermsInclude, subquery.searchTermsAdvice, subquery.searchTermsExclude);
break outer;
}
if (subqueryCount >= 100)
break;
}
}
return results;
}
private int fetchSizeMultiplier(SearchParameters params, SearchIndexSearchTerms terms) {
if (terms.size() == 1) {
return 4;
}
return 1;
}
private void logSearchTerms(SearchSubquery subquery, SearchIndexSearchTerms searchTerms) {
if (!logger.isInfoEnabled(queryMarker)) {
@ -193,23 +205,25 @@ public class IndexQueryService {
logger.info(queryMarker, "{} -> {} E", excludes.get(i), searchTerms.excludes().getInt(i));
}
for (int i = 0; i < subquery.searchTermsPriority.size(); i++) {
logger.info(queryMarker, "{} -> {} p", priority.get(i), searchTerms.priority().getInt(i));
logger.info(queryMarker, "{} -> {} P", priority.get(i), searchTerms.priority().getInt(i));
}
}
private TLongArrayList executeQuery(IndexQuery query, SearchParameters params)
private TLongArrayList executeQuery(IndexQuery query, SearchParameters params, int fetchSizeMultiplier)
{
final TLongArrayList results = new TLongArrayList(params.fetchSize);
final LongQueryBuffer buffer = new LongQueryBuffer(params.fetchSize);
final int fetchSize = params.fetchSize * fetchSizeMultiplier;
final TLongArrayList results = new TLongArrayList(fetchSize);
final LongQueryBuffer buffer = new LongQueryBuffer(fetchSize);
while (query.hasMore()
&& results.size() < params.fetchSize
&& results.size() < fetchSize
&& params.budget.hasTimeLeft())
{
buffer.reset();
query.getMoreResults(buffer);
for (int i = 0; i < buffer.size() && results.size() < params.fetchSize; i++) {
for (int i = 0; i < buffer.size() && results.size() < fetchSize; i++) {
results.add(buffer.data[i]);
}
}
@ -219,32 +233,22 @@ public class IndexQueryService {
return results;
}
private ArrayList<SearchResultItem> calculateResultScores(SearchParameters params, TLongList resultIds) {
private List<SearchResultItem> calculateResultScores(SearchParameters params, ResultRankingContext rankingContext, TLongList resultIds) {
final var evaluator = new IndexResultValuator(metadataService, resultIds, params.subqueries, params.queryParams);
final var evaluator = new IndexResultValuator(metadataService,
resultIds,
rankingContext,
params.subqueries,
params.queryParams);
ArrayList<SearchResultItem> items = new ArrayList<>(resultIds.size());
// Note, this is a pre-sorting the result IDs. This is a performance optimization, as it will cluster
// disk access to adjacent parts of the forward index when fetching metadata
//
// This is *not* where the actual search results are sorted
// Sort the ids for more favorable access patterns on disk
resultIds.sort();
resultIds.forEach(id -> {
var item = evaluator.calculatePreliminaryScore(id);
if (!item.getScore().isEmpty()) {
items.add(item);
}
return true;
});
logger.info(queryMarker, "After filtering: {} -> {}", resultIds.size(), items.size());
return items;
return Arrays.stream(resultIds.toArray())
.parallel()
.mapToObj(evaluator::calculatePreliminaryScore)
.filter(score -> !score.getScore().isEmpty())
.collect(Collectors.toList());
}
private List<SearchResultItem> selectBestResults(SearchParameters params, List<SearchResultItem> results) {

View File

@ -3,6 +3,7 @@ package nu.marginalia.index.svc;
import gnu.trove.set.hash.TLongHashSet;
import nu.marginalia.index.client.model.query.SearchSpecification;
import nu.marginalia.index.client.model.query.SearchSubquery;
import nu.marginalia.index.client.model.results.ResultRankingParameters;
import nu.marginalia.index.index.SearchIndex;
import nu.marginalia.index.index.SearchIndexSearchTerms;
import nu.marginalia.index.query.IndexQuery;
@ -21,6 +22,7 @@ public class SearchParameters {
final IndexSearchBudget budget;
final List<SearchSubquery> subqueries;
final IndexQueryParams queryParams;
final ResultRankingParameters rankingParams;
final int limitByDomain;
final int limitTotal;
@ -56,6 +58,8 @@ public class SearchParameters {
specsSet.rank,
searchSet,
specsSet.queryStrategy);
rankingParams = specsSet.rankingParams;
}
List<IndexQuery> createIndexQueries(SearchIndex index, SearchIndexSearchTerms terms) {

View File

@ -5,6 +5,7 @@ import com.google.inject.Inject;
import nu.marginalia.index.client.model.query.SearchSpecification;
import nu.marginalia.index.client.model.query.SearchSubquery;
import nu.marginalia.index.client.model.query.SearchSetIdentifier;
import nu.marginalia.index.client.model.results.ResultRankingParameters;
import nu.marginalia.index.client.model.results.SearchResultItem;
import nu.marginalia.index.index.SearchIndex;
import nu.marginalia.index.journal.model.IndexJournalEntryData;
@ -86,6 +87,7 @@ public class IndexQueryServiceIntegrationTest {
.quality(SpecificationLimit.none())
.size(SpecificationLimit.none())
.rank(SpecificationLimit.none())
.rankingParams(ResultRankingParameters.sensibleDefaults())
.domains(new ArrayList<>())
.searchSetIdentifier(SearchSetIdentifier.NONE)
.subqueries(List.of(new SearchSubquery(
@ -117,6 +119,7 @@ public class IndexQueryServiceIntegrationTest {
.quality(SpecificationLimit.none())
.size(SpecificationLimit.none())
.rank(SpecificationLimit.none())
.rankingParams(ResultRankingParameters.sensibleDefaults())
.queryStrategy(QueryStrategy.SENTENCE)
.domains(List.of(2))
.subqueries(List.of(new SearchSubquery(
@ -144,6 +147,7 @@ public class IndexQueryServiceIntegrationTest {
.rank(SpecificationLimit.none())
.queryStrategy(QueryStrategy.SENTENCE)
.searchSetIdentifier(SearchSetIdentifier.NONE)
.rankingParams(ResultRankingParameters.sensibleDefaults())
.subqueries(List.of(new SearchSubquery(
List.of("4"), Collections.emptyList(), Collections.emptyList(), Collections.emptyList()
))
@ -164,12 +168,12 @@ public class IndexQueryServiceIntegrationTest {
long fullId = id | ((long) (32 - (id % 32)) << 32);
var header = new IndexJournalEntryHeader(factors.length, fullId, new DocumentMetadata(0, 0, 0, id % 5, id, id % 20, (byte) 0).encode());
var header = new IndexJournalEntryHeader(factors.length, fullId, new DocumentMetadata(0, 0, 0, 0, id % 5, id, id % 20, (byte) 0).encode());
long[] data = new long[factors.length*2];
for (int i = 0; i < factors.length; i++) {
data[2*i] = keywordLexicon.getOrInsert(Integer.toString(factors[i]));
data[2*i + 1] = new WordMetadata(i, i, EnumSet.of(WordFlags.Title)).encode();
data[2*i + 1] = new WordMetadata(i, EnumSet.of(WordFlags.Title)).encode();
}
indexJournalWriter.put(header, new IndexJournalEntryData(data));
@ -182,7 +186,7 @@ public class IndexQueryServiceIntegrationTest {
long[] data = new long[factors.length*2];
for (int i = 0; i < factors.length; i++) {
data[2*i] = keywordLexicon.getOrInsert(Integer.toString(factors[i]));
data[2*i + 1] = new WordMetadata(i % 20, i, EnumSet.of(WordFlags.Title)).encode();
data[2*i + 1] = new WordMetadata(i, EnumSet.of(WordFlags.Title)).encode();
}
indexJournalWriter.put(header, new IndexJournalEntryData(data));

View File

@ -5,6 +5,7 @@ import com.google.inject.Singleton;
import nu.marginalia.LanguageModels;
import nu.marginalia.index.client.model.query.SearchSpecification;
import nu.marginalia.index.client.model.query.SearchSubquery;
import nu.marginalia.index.client.model.results.ResultRankingParameters;
import nu.marginalia.index.query.limit.QueryLimits;
import nu.marginalia.index.query.limit.QueryStrategy;
import nu.marginalia.index.query.limit.SpecificationLimit;
@ -89,11 +90,12 @@ public class QueryFactory {
.subqueries(sqs)
.domains(Collections.emptyList())
.searchSetIdentifier(profile.searchSetIdentifier)
.queryLimits(new QueryLimits(limitPerDomain, limitTotal, 150, 2048))
.queryLimits(new QueryLimits(limitPerDomain, limitTotal, 250, 8192))
.humanQuery("")
.year(SpecificationLimit.none())
.size(SpecificationLimit.none())
.rank(SpecificationLimit.none())
.rankingParams(ResultRankingParameters.sensibleDefaults())
.quality(SpecificationLimit.none())
.queryStrategy(QueryStrategy.AUTO)
.build();
@ -119,9 +121,10 @@ public class QueryFactory {
List<String> searchTermsHuman = new ArrayList<>();
List<String> problems = new ArrayList<>();
String domain = null;
QueryStrategy queryStrategy = QueryStrategy.AUTO;
String near = null,
domain = null;
var basicQuery = queryParser.parse(query);
@ -130,10 +133,8 @@ public class QueryFactory {
basicQuery.clear();
}
SpecificationLimit qualityLimit = profile.getQualityLimit();
SpecificationLimit year = profile.getYearLimit();
SpecificationLimit size = profile.getSizeLimit();
SpecificationLimit rank = SpecificationLimit.none();
QueryLimitsAccumulator qualityLimits = new QueryLimitsAccumulator(profile);
for (Token t : basicQuery) {
if (t.type == TokenType.QUOT_TERM || t.type == TokenType.LITERAL_TERM) {
@ -144,74 +145,20 @@ public class QueryFactory {
searchTermsHuman.addAll(toHumanSearchTerms(t));
analyzeSearchTerm(problems, t);
}
if (t.type == TokenType.QUALITY_TERM) {
qualityLimit = parseSpecificationLimit(t.str);
}
if (t.type == TokenType.YEAR_TERM) {
year = parseSpecificationLimit(t.str);
}
if (t.type == TokenType.SIZE_TERM) {
size = parseSpecificationLimit(t.str);
}
if (t.type == TokenType.RANK_TERM) {
rank = parseSpecificationLimit(t.str);
}
if (t.type == TokenType.QS_TERM) {
queryStrategy = parseQueryStrategy(t.str);
}
t.visit(qualityLimits);
}
var queryPermutations = queryPermutation.permuteQueriesNew(basicQuery);
List<SearchSubquery> subqueries = new ArrayList<>();
String near = profile.getNearDomain();
for (var parts : queryPermutations) {
List<String> searchTermsExclude = new ArrayList<>();
List<String> searchTermsInclude = new ArrayList<>();
List<String> searchTermsAdvice = new ArrayList<>();
List<String> searchTermsPriority = new ArrayList<>();
QuerySearchTermsAccumulator termsAccumulator = new QuerySearchTermsAccumulator(profile, parts);
for (Token t : parts) {
switch (t.type) {
case EXCLUDE_TERM:
searchTermsExclude.add(t.str);
break;
case ADVICE_TERM:
searchTermsAdvice.add(t.str);
if (t.str.toLowerCase().startsWith("site:")) {
domain = t.str.substring("site:".length());
}
break;
case PRIORTY_TERM:
searchTermsPriority.add(t.str);
break;
case LITERAL_TERM: // fallthrough;
case QUOT_TERM:
searchTermsInclude.add(t.str);
break;
case QUALITY_TERM:
case YEAR_TERM:
case SIZE_TERM:
case RANK_TERM:
case QS_TERM:
break; //
case NEAR_TERM:
near = t.str;
break;
SearchSubquery subquery = termsAccumulator.createSubquery();
default:
logger.warn("Unexpected token type {}", t);
}
}
if (searchTermsInclude.isEmpty() && !searchTermsAdvice.isEmpty()) {
searchTermsInclude.addAll(searchTermsAdvice);
searchTermsAdvice.clear();
}
SearchSubquery subquery = new SearchSubquery(searchTermsInclude, searchTermsExclude, searchTermsAdvice, searchTermsPriority);
near = termsAccumulator.near;
domain = termsAccumulator.domain;
params.profile().addTacitTerms(subquery);
params.jsSetting().addTacitTerms(subquery);
@ -238,12 +185,13 @@ public class QueryFactory {
.subqueries(subqueries)
.queryLimits(new QueryLimits(domainLimit, 100, 250, 4096))
.humanQuery(query)
.quality(qualityLimit)
.year(year)
.size(size)
.rank(rank)
.quality(qualityLimits.qualityLimit)
.year(qualityLimits.year)
.size(qualityLimits.size)
.rank(qualityLimits.rank)
.domains(domains)
.queryStrategy(queryStrategy)
.rankingParams(ResultRankingParameters.sensibleDefaults())
.queryStrategy(qualityLimits.queryStrategy)
.searchSetIdentifier(profile.searchSetIdentifier);
SearchSpecification specs = specsBuilder.build();
@ -251,36 +199,8 @@ public class QueryFactory {
return new SearchQuery(specs, searchTermsHuman, domain);
}
private SpecificationLimit parseSpecificationLimit(String str) {
int startChar = str.charAt(0);
int val = Integer.parseInt(str.substring(1));
if (startChar == '=') {
return SpecificationLimit.equals(val);
}
else if (startChar == '<') {
return SpecificationLimit.lessThan(val);
}
else if (startChar == '>') {
return SpecificationLimit.greaterThan(val);
}
else {
return SpecificationLimit.none();
}
}
private QueryStrategy parseQueryStrategy(String str) {
return switch (str.toUpperCase()) {
case "RF_TITLE" -> QueryStrategy.REQUIRE_FIELD_TITLE;
case "RF_SUBJECT" -> QueryStrategy.REQUIRE_FIELD_SUBJECT;
case "RF_SITE" -> QueryStrategy.REQUIRE_FIELD_SITE;
case "RF_URL" -> QueryStrategy.REQUIRE_FIELD_URL;
case "RF_DOMAIN" -> QueryStrategy.REQUIRE_FIELD_DOMAIN;
case "SENTENCE" -> QueryStrategy.SENTENCE;
case "TOPIC" -> QueryStrategy.TOPIC;
default -> QueryStrategy.AUTO;
};
}
private String normalizeDomainName(String str) {
return str.toLowerCase();

View File

@ -0,0 +1,95 @@
package nu.marginalia.search.query;
import nu.marginalia.index.query.limit.QueryStrategy;
import nu.marginalia.index.query.limit.SpecificationLimit;
import nu.marginalia.query_parser.token.Token;
import nu.marginalia.query_parser.token.TokenVisitor;
import nu.marginalia.search.model.SearchProfile;
public class QueryLimitsAccumulator implements TokenVisitor {
public SpecificationLimit qualityLimit;
public SpecificationLimit year;
public SpecificationLimit size;
public SpecificationLimit rank;
public QueryStrategy queryStrategy = QueryStrategy.AUTO;
public QueryLimitsAccumulator(SearchProfile profile) {
qualityLimit = profile.getQualityLimit();
year = profile.getYearLimit();
size = profile.getSizeLimit();
rank = SpecificationLimit.none();
}
private SpecificationLimit parseSpecificationLimit(String str) {
int startChar = str.charAt(0);
int val = Integer.parseInt(str.substring(1));
if (startChar == '=') {
return SpecificationLimit.equals(val);
} else if (startChar == '<') {
return SpecificationLimit.lessThan(val);
} else if (startChar == '>') {
return SpecificationLimit.greaterThan(val);
} else {
return SpecificationLimit.none();
}
}
private QueryStrategy parseQueryStrategy(String str) {
return switch (str.toUpperCase()) {
case "RF_TITLE" -> QueryStrategy.REQUIRE_FIELD_TITLE;
case "RF_SUBJECT" -> QueryStrategy.REQUIRE_FIELD_SUBJECT;
case "RF_SITE" -> QueryStrategy.REQUIRE_FIELD_SITE;
case "RF_URL" -> QueryStrategy.REQUIRE_FIELD_URL;
case "RF_DOMAIN" -> QueryStrategy.REQUIRE_FIELD_DOMAIN;
case "SENTENCE" -> QueryStrategy.SENTENCE;
case "TOPIC" -> QueryStrategy.TOPIC;
default -> QueryStrategy.AUTO;
};
}
@Override
public void onYearTerm(Token token) {
year = parseSpecificationLimit(token.str);
}
@Override
public void onSizeTerm(Token token) {
size = parseSpecificationLimit(token.str);
}
@Override
public void onRankTerm(Token token) {
rank = parseSpecificationLimit(token.str);
}
@Override
public void onQualityTerm(Token token) {
qualityLimit = parseSpecificationLimit(token.str);
}
@Override
public void onQsTerm(Token token) {
queryStrategy = parseQueryStrategy(token.str);
}
@Override
public void onLiteralTerm(Token token) {}
@Override
public void onQuotTerm(Token token) {}
@Override
public void onExcludeTerm(Token token) {}
@Override
public void onPriorityTerm(Token token) {}
@Override
public void onAdviceTerm(Token token) {}
@Override
public void onNearTerm(Token token) {}
}

View File

@ -0,0 +1,104 @@
package nu.marginalia.search.query;
import nu.marginalia.index.client.model.query.SearchSubquery;
import nu.marginalia.query_parser.token.Token;
import nu.marginalia.query_parser.token.TokenVisitor;
import nu.marginalia.search.model.SearchProfile;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class QuerySearchTermsAccumulator implements TokenVisitor {
public List<String> searchTermsExclude = new ArrayList<>();
public List<String> searchTermsInclude = new ArrayList<>();
public List<String> searchTermsAdvice = new ArrayList<>();
public List<String> searchTermsPriority = new ArrayList<>();
public String near;
public String domain;
public SearchSubquery createSubquery() {
return new SearchSubquery(searchTermsInclude, searchTermsExclude, searchTermsAdvice, searchTermsPriority);
}
public QuerySearchTermsAccumulator(SearchProfile profile, List<Token> parts) {
near = profile.getNearDomain();
for (Token t : parts) {
t.visit(this);
}
if (searchTermsInclude.isEmpty() && !searchTermsAdvice.isEmpty()) {
searchTermsInclude.addAll(searchTermsAdvice);
searchTermsAdvice.clear();
}
}
@Override
public void onLiteralTerm(Token token) {
searchTermsInclude.add(token.str);
}
@Override
public void onQuotTerm(Token token) {
String[] parts = token.str.split("_");
if (parts.length > 1) {
searchTermsAdvice.add(token.str);
searchTermsInclude.addAll(Arrays.asList(parts));
}
else {
searchTermsInclude.add(token.str);
}
}
@Override
public void onExcludeTerm(Token token) {
searchTermsExclude.add(token.str);
}
@Override
public void onPriorityTerm(Token token) {
searchTermsPriority.add(token.str);
}
@Override
public void onAdviceTerm(Token token) {
searchTermsAdvice.add(token.str);
if (token.str.toLowerCase().startsWith("site:")) {
domain = token.str.substring("site:".length());
}
}
@Override
public void onNearTerm(Token token) {
near = token.str;
}
@Override
public void onYearTerm(Token token) {
}
@Override
public void onSizeTerm(Token token) {
}
@Override
public void onRankTerm(Token token) {
}
@Override
public void onQualityTerm(Token token) {
}
@Override
public void onQsTerm(Token token) {
}
}

View File

@ -3,9 +3,9 @@ package nu.marginalia.search.results;
import com.google.inject.Inject;
import gnu.trove.list.array.TIntArrayList;
import gnu.trove.map.hash.TIntObjectHashMap;
import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
import it.unimi.dsi.fastutil.ints.Int2LongArrayMap;
import nu.marginalia.bbpc.BrailleBlockPunchCards;
import nu.marginalia.index.client.model.results.SearchResultRankingContext;
import nu.marginalia.index.client.model.results.ResultRankingContext;
import nu.marginalia.index.client.model.results.SearchResultSet;
import nu.marginalia.ranking.ResultValuator;
import nu.marginalia.search.db.DbUrlDetailsQuery;
@ -14,7 +14,6 @@ import nu.marginalia.model.crawl.DomainIndexingState;
import nu.marginalia.model.id.EdgeIdList;
import nu.marginalia.index.client.model.results.SearchResultItem;
import nu.marginalia.search.model.UrlDetails;
import nu.marginalia.search.query.model.SearchQuery;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -80,7 +79,7 @@ public class SearchResultDecorator {
}
private String getPositionsString(SearchResultItem resultItem) {
Int2IntArrayMap positionsPerSet = new Int2IntArrayMap(8);
Int2LongArrayMap positionsPerSet = new Int2LongArrayMap(8);
for (var score : resultItem.keywordScores) {
if (!score.isKeywordRegular()) {
@ -89,26 +88,25 @@ public class SearchResultDecorator {
positionsPerSet.merge(score.subquery(), score.positions(), this::and);
}
int bits = positionsPerSet.values().intStream().reduce(this::or).orElse(0);
long bits = positionsPerSet.values().longStream().reduce(this::or).orElse(0);
return BrailleBlockPunchCards.printBits(bits, 32);
return BrailleBlockPunchCards.printBits(bits, 56);
}
private int and(int a, int b) {
private long and(long a, long b) {
return a & b;
}
private int or(int a, int b) {
private long or(long a, long b) {
return a | b;
}
private double calculateTermScore(SearchResultItem resultItem, UrlDetails details, SearchResultRankingContext rankingContext) {
private double calculateTermScore(SearchResultItem resultItem, UrlDetails details, ResultRankingContext rankingContext) {
final double statePenalty = (details.domainState == DomainIndexingState.SPECIAL) ? 1.25 : 0;
final double value = valuator.calculateSearchResultValue(resultItem.keywordScores,
details.words,
details.title.length(),
rankingContext);
return value + statePenalty;

View File

@ -73,7 +73,7 @@ public class SearchApiQueryService {
continue outer;
Set<String> flags = metadata.flagSet().stream().map(Object::toString).collect(Collectors.toSet());
lst.add(new ApiSearchResultQueryDetails(entry.keyword, metadata.tfIdf(), Integer.bitCount(metadata.positions()), flags));
lst.add(new ApiSearchResultQueryDetails(entry.keyword, Long.bitCount(metadata.positions()), flags));
}
details.add(lst);
}

View File

@ -11,6 +11,10 @@ import nu.marginalia.search.results.SearchResultDecorator;
import nu.marginalia.search.results.UrlDeduplicator;
import nu.marginalia.client.Context;
import nu.marginalia.search.query.model.SearchQuery;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;
import java.util.*;
import java.util.regex.Pattern;
@ -21,6 +25,8 @@ public class SearchQueryIndexService {
private final Comparator<UrlDetails> resultListComparator;
private final IndexClient indexClient;
private final SearchQueryCountService searchVisitorCount;
private final Marker queryMarker = MarkerFactory.getMarker("QUERY");
private final Logger logger = LoggerFactory.getLogger(getClass());
@Inject
public SearchQueryIndexService(SearchResultDecorator resultDecorator,
@ -54,6 +60,7 @@ public class SearchQueryIndexService {
UrlDeduplicator deduplicator = new UrlDeduplicator(limits.resultsByDomain());
List<UrlDetails> retList = new ArrayList<>(limits.resultsTotal());
int dedupCount = 0;
for (var item : decoratedResults) {
if (retList.size() >= limits.resultsTotal())
break;
@ -61,6 +68,13 @@ public class SearchQueryIndexService {
if (!deduplicator.shouldRemove(item)) {
retList.add(item);
}
else {
dedupCount ++;
}
}
if (dedupCount > 0) {
logger.info(queryMarker, "Deduplicator ate {} results", dedupCount);
}
return retList;

View File

@ -1,14 +1,8 @@
/* If you need to borrow something from below, that's fine */
.extra a {
background: #ccc linear-gradient(45deg, rgba(255,220,220,1) 0%, rgba(219,255,196,1) 50%, rgba(212,216,255,1) 100%);
color: #000;
padding: 0.5ch;
border-radius: 0.5ch;
text-decoration: none;
border: 3px outset #000;
word-break: none;
white-space: nowrap;
float: right;
nav a.extra {
background: #ccc linear-gradient(45deg, rgba(255,100,100,1) 0%, rgba(100,255,100,1) 50%, rgba(100,100,255,1) 100%);
color: black;
}
.extra a:active {
@ -50,7 +44,6 @@ header nav a {
text-decoration: none;
color: #000;
margin-right: 1ch;
padding: .5ch;
display: inline-block;
}
@ -495,7 +488,7 @@ a.underline {
flex-direction: column;
}
header nav a {
padding: 1ch !important;
padding: 0.75ch !important;
}
.card {
@ -520,12 +513,12 @@ a.underline {
/* https://www.youtube.com/watch?v=v0nmHymgM7Y */
@media (prefers-color-scheme: dark) {
.extra a {
background: #000 linear-gradient(45deg, rgba(135,93,93,1) 0%, rgba(106,135,87,1) 50%, rgba(76,83,118,1) 100%);
font-weight: bold;
color: #fff;
border: 3px outset #000;
nav a.extra {
background: #ccc linear-gradient(45deg, rgba(100,0,0,1) 0%, rgba(0,100,0,1) 50%, rgba(0,0,100,1) 100%);
color: white;
}
.positions {
box-shadow: 0px 0px 2px #222;
background-color: #222;

View File

@ -30,8 +30,5 @@
<option {{#eq js "yes-js"}}selected{{/eq}} value="yes-js">Require JS</option>
</select>
</div>
<div class="extra">
<a href="/explore/random">Random Websites</a>
</div>
</section>
</form>

View File

@ -4,5 +4,6 @@
<a href="https://www.marginalia.nu/">Marginalia</a>
<a href="https://memex.marginalia.nu/projects/edge/about.gmi">About</a>
<a href="https://memex.marginalia.nu/projects/edge/supporting.gmi">Support</a>
<a class="extra" href="https://search.marginalia.nu/explore/random">Random</a>
</nav>
</header>