Merged branch 'jetty-9.4.x' into 'jetty-10.0.x'.

This commit is contained in:
Simone Bordet 2019-06-03 17:31:50 +02:00
commit 2ba4cb2924
7 changed files with 471 additions and 115 deletions

View File

@ -276,12 +276,9 @@ public class HttpField
public boolean isSameName(HttpField field)
{
@SuppressWarnings("ReferenceEquality")
boolean sameObject = (field==this);
if (field==null)
return false;
if (sameObject)
if (field==this)
return true;
if (_header!=null && _header==field.getHeader())
return true;

View File

@ -32,7 +32,6 @@ import java.util.Set;
import java.util.StringTokenizer;
import java.util.function.ToIntFunction;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.eclipse.jetty.util.QuotedStringTokenizer;
import org.eclipse.jetty.util.log.Log;
@ -44,7 +43,7 @@ import org.eclipse.jetty.util.log.Logger;
*
* <p>This class is not synchronized as it is expected that modifications will only be performed by a
* single thread.
*
*
* <p>The cookie handling provided by this class is guided by the Servlet specification and RFC6265.
*
*/
@ -54,33 +53,33 @@ public class HttpFields implements Iterable<HttpField>
private HttpField[] _fields;
private int _size;
/**
* Initialize an empty HttpFields.
*/
public HttpFields()
{
_fields=new HttpField[20];
this(16); // Based on small sample of Chrome requests.
}
/**
* Initialize an empty HttpFields.
*
*
* @param capacity the capacity of the http fields
*/
public HttpFields(int capacity)
{
_fields=new HttpField[capacity];
}
/**
* Initialize HttpFields from copy.
*
*
* @param fields the fields to copy data from
*/
public HttpFields(HttpFields fields)
{
_fields=Arrays.copyOf(fields._fields,fields._fields.length+10);
_fields=Arrays.copyOf(fields._fields,fields._fields.length);
_size=fields._size;
}
@ -88,22 +87,21 @@ public class HttpFields implements Iterable<HttpField>
{
return _size;
}
@Override
public Iterator<HttpField> iterator()
{
return new Itr();
return listIterator();
}
public ListIterator<HttpField> listIterator()
{
return new Itr();
return new ListItr();
}
public Stream<HttpField> stream()
{
return StreamSupport.stream(Arrays.spliterator(_fields,0,_size),false);
return Arrays.stream(_fields).limit(_size);
}
/**
@ -112,13 +110,15 @@ public class HttpFields implements Iterable<HttpField>
*/
public Set<String> getFieldNamesCollection()
{
final Set<String> set = new HashSet<>(_size);
for (HttpField f : this)
Set<String> set = null;
for (int i=0;i<_size;i++)
{
if (f!=null)
set.add(f.getName());
HttpField f=_fields[i];
if (set==null)
set = new HashSet<>();
set.add(f.getName());
}
return set;
return set==null?Collections.emptySet():set;
}
/**
@ -133,7 +133,7 @@ public class HttpFields implements Iterable<HttpField>
/**
* Get a Field by index.
* @param index the field index
* @param index the field index
* @return A Field value or null if the Field value has not been set
*/
public HttpField getField(int index)
@ -165,6 +165,22 @@ public class HttpFields implements Iterable<HttpField>
return null;
}
public List<HttpField> getFields(HttpHeader header)
{
List<HttpField> fields = null;
for (int i=0;i<_size;i++)
{
HttpField f=_fields[i];
if (f.getHeader()==header)
{
if (fields==null)
fields = new ArrayList<>();
fields.add(f);
}
}
return fields==null?Collections.emptyList():fields;
}
public boolean contains(HttpField field)
{
for (int i=_size;i-->0;)
@ -186,7 +202,7 @@ public class HttpFields implements Iterable<HttpField>
}
return false;
}
public boolean contains(String name, String value)
{
for (int i=_size;i-->0;)
@ -208,7 +224,7 @@ public class HttpFields implements Iterable<HttpField>
}
return false;
}
public boolean containsKey(String name)
{
for (int i=_size;i-->0;)
@ -251,24 +267,30 @@ public class HttpFields implements Iterable<HttpField>
public List<String> getValuesList(HttpHeader header)
{
final List<String> list = new ArrayList<>();
for (HttpField f : this)
if (f.getHeader()==header)
for (int i=0;i<_size;i++)
{
HttpField f = _fields[i];
if (f.getHeader() == header)
list.add(f.getValue());
}
return list;
}
/**
* Get multiple header of the same name
*
*
* @return List the header values
* @param name the case-insensitive field name
*/
public List<String> getValuesList(String name)
{
final List<String> list = new ArrayList<>();
for (HttpField f : this)
for (int i=0;i<_size;i++)
{
HttpField f = _fields[i];
if (f.getName().equalsIgnoreCase(name))
list.add(f.getValue());
}
return list;
}
@ -283,8 +305,9 @@ public class HttpFields implements Iterable<HttpField>
public boolean addCSV(HttpHeader header,String... values)
{
QuotedCSV existing = null;
for (HttpField f : this)
for (int i=0;i<_size;i++)
{
HttpField f = _fields[i];
if (f.getHeader()==header)
{
if (existing==null)
@ -292,7 +315,7 @@ public class HttpFields implements Iterable<HttpField>
existing.addValue(f.getValue());
}
}
String value = addCSV(existing,values);
if (value!=null)
{
@ -301,7 +324,7 @@ public class HttpFields implements Iterable<HttpField>
}
return false;
}
/**
* Add comma separated values, but only if not already
* present.
@ -312,8 +335,9 @@ public class HttpFields implements Iterable<HttpField>
public boolean addCSV(String name,String... values)
{
QuotedCSV existing = null;
for (HttpField f : this)
for (int i=0;i<_size;i++)
{
HttpField f = _fields[i];
if (f.getName().equalsIgnoreCase(name))
{
if (existing==null)
@ -330,14 +354,14 @@ public class HttpFields implements Iterable<HttpField>
return false;
}
protected String addCSV(QuotedCSV existing,String... values)
protected String addCSV(QuotedCSV existing, String... values)
{
// remove any existing values from the new values
boolean add = true;
if (existing!=null && !existing.isEmpty())
{
add = false;
for (int i=values.length;i-->0;)
{
String unquoted = QuotedCSV.unquote(values[i]);
@ -347,7 +371,7 @@ public class HttpFields implements Iterable<HttpField>
add = true;
}
}
if (add)
{
StringBuilder value = new StringBuilder();
@ -362,12 +386,12 @@ public class HttpFields implements Iterable<HttpField>
if (value.length()>0)
return value.toString();
}
return null;
}
/**
* Get multiple field values of the same name, split
* Get multiple field values of the same name, split
* as a {@link QuotedCSV}
*
* @return List the values with OWS stripped
@ -481,7 +505,7 @@ public class HttpFields implements Iterable<HttpField>
for (int i=0;i<_size;i++)
{
final HttpField f = _fields[i];
if (f.getName().equalsIgnoreCase(name) && f.getValue()!=null)
{
final int first=i;
@ -495,7 +519,7 @@ public class HttpFields implements Iterable<HttpField>
{
if (field==null)
{
while (i<_size)
while (i<_size)
{
field=_fields[i++];
if (field.getName().equalsIgnoreCase(name) && field.getValue()!=null)
@ -548,7 +572,7 @@ public class HttpFields implements Iterable<HttpField>
if (!put)
add(field);
}
/**
* Set a field.
*
@ -840,7 +864,7 @@ public class HttpFields implements Iterable<HttpField>
{
_size=0;
}
public void add(HttpField field)
{
if (field!=null)
@ -938,36 +962,36 @@ public class HttpFields implements Iterable<HttpField>
return value.substring(0, i).trim();
}
private class Itr implements ListIterator<HttpField>
private class ListItr implements ListIterator<HttpField>
{
int _cursor; // index of next element to return
int _last=-1;
int _current =-1;
@Override
public boolean hasNext()
public boolean hasNext()
{
return _cursor != _size;
}
@Override
public HttpField next()
public HttpField next()
{
int i = _cursor;
if (i >= _size)
if (_cursor == _size)
throw new NoSuchElementException();
_cursor = i + 1;
return _fields[_last=i];
_current = _cursor++;
return _fields[_current];
}
@Override
public void remove()
public void remove()
{
if (_last<0)
if (_current <0)
throw new IllegalStateException();
System.arraycopy(_fields,_last+1,_fields,_last,--_size-_last);
_cursor=_last;
_last=-1;
_size--;
System.arraycopy(_fields, _current +1,_fields, _current,_size- _current);
_fields[_size]=null;
_cursor= _current;
_current =-1;
}
@Override
@ -981,7 +1005,8 @@ public class HttpFields implements Iterable<HttpField>
{
if (_cursor == 0)
throw new NoSuchElementException();
return _fields[_last=--_cursor];
_current = --_cursor;
return _fields[_current];
}
@Override
@ -998,10 +1023,10 @@ public class HttpFields implements Iterable<HttpField>
@Override
public void set(HttpField field)
{
if (_last<0)
{
if (_current <0)
throw new IllegalStateException();
_fields[_last] = field;
_fields[_current] = field;
}
@Override
@ -1010,8 +1035,7 @@ public class HttpFields implements Iterable<HttpField>
_fields = Arrays.copyOf(_fields,_fields.length+1);
System.arraycopy(_fields,_cursor,_fields,_cursor+1,_size++);
_fields[_cursor++] = field;
_last=-1;
_current =-1;
}
}
}

View File

@ -39,7 +39,7 @@ public class MetaDataBuilder
private HostPortHttpField _authority;
private String _path;
private long _contentLength=Long.MIN_VALUE;
private HttpFields _fields = new HttpFields(10);
private HttpFields _fields = new HttpFields();
private HpackException.StreamException _streamException;
private boolean _request;
private boolean _response;
@ -255,7 +255,7 @@ public class MetaDataBuilder
}
finally
{
_fields = new HttpFields(Math.max(10, fields.size() + 5));
_fields = new HttpFields(Math.max(16, fields.size() + 5));
_request = false;
_response = false;
_status = null;

View File

@ -203,6 +203,7 @@ public class Request implements HttpServletRequest
private boolean _requestedSessionIdFromCookie = false;
private Attributes _attributes;
private Authentication _authentication;
private String _contentType;
private String _characterEncoding;
private ContextHandler.Context _context;
private Cookies _cookies;
@ -454,8 +455,8 @@ public class Request implements HttpServletRequest
int contentLength = getContentLength();
if (contentLength != 0 && _inputState == __NONE)
{
contentType = HttpFields.valueParameters(contentType, null);
if (MimeTypes.Type.FORM_ENCODED.is(contentType) &&
String baseType = HttpFields.valueParameters(contentType, null);
if (MimeTypes.Type.FORM_ENCODED.is(baseType) &&
_channel.getHttpConfiguration().isFormEncodedMethod(getMethod()))
{
if (_metaData!=null)
@ -466,7 +467,7 @@ public class Request implements HttpServletRequest
}
extractFormParameters(_contentParameters);
}
else if (MimeTypes.Type.MULTIPART_FORM_DATA.is(contentType) &&
else if (MimeTypes.Type.MULTIPART_FORM_DATA.is(baseType) &&
getAttribute(__MULTIPART_CONFIG_ELEMENT) != null &&
_multiParts == null)
{
@ -670,7 +671,16 @@ public class Request implements HttpServletRequest
public String getCharacterEncoding()
{
if (_characterEncoding==null)
getContentType();
{
String contentType = getContentType();
if (contentType!=null)
{
MimeTypes.Type mime = MimeTypes.CACHE.get(contentType);
String charset = (mime == null || mime.getCharset() == null) ? MimeTypes.getCharsetFromContentType(contentType) : mime.getCharset().toString();
if (charset != null)
_characterEncoding=charset;
}
}
return _characterEncoding;
}
@ -726,16 +736,12 @@ public class Request implements HttpServletRequest
@Override
public String getContentType()
{
MetaData.Request metadata = _metaData;
String content_type = metadata==null?null:metadata.getFields().get(HttpHeader.CONTENT_TYPE);
if (_characterEncoding==null && content_type!=null)
if (_contentType==null)
{
MimeTypes.Type mime = MimeTypes.CACHE.get(content_type);
String charset = (mime == null || mime.getCharset() == null) ? MimeTypes.getCharsetFromContentType(content_type) : mime.getCharset().toString();
if (charset != null)
_characterEncoding=charset;
MetaData.Request metadata = _metaData;
_contentType = metadata == null ? null : metadata.getFields().get(HttpHeader.CONTENT_TYPE);
}
return content_type;
return _contentType;
}
/* ------------------------------------------------------------ */
@ -1818,6 +1824,7 @@ public class Request implements HttpServletRequest
protected void recycle()
{
_metaData=null;
_originalUri=null;
if (_context != null)
throw new IllegalStateException("Request in context!");
@ -1847,6 +1854,7 @@ public class Request implements HttpServletRequest
_handled = false;
if (_attributes != null)
_attributes.clearAttributes();
_contentType = null;
_characterEncoding = null;
_contextPath = null;
if (_cookies != null)
@ -2005,10 +2013,8 @@ public class Request implements HttpServletRequest
* @see javax.servlet.ServletRequest#getContentType()
*/
public void setContentType(String contentType)
{
MetaData.Request metadata = _metaData;
if (metadata!=null)
metadata.getFields().put(HttpHeader.CONTENT_TYPE,contentType);
{
_contentType = contentType;
}
/* ------------------------------------------------------------ */
@ -2319,8 +2325,8 @@ public class Request implements HttpServletRequest
@Override
public Collection<Part> getParts() throws IOException, ServletException
{
if (getContentType() == null ||
!MimeTypes.Type.MULTIPART_FORM_DATA.is(HttpFields.valueParameters(getContentType(),null)))
String contentType = getContentType();
if (contentType == null || !MimeTypes.Type.MULTIPART_FORM_DATA.is(HttpFields.valueParameters(contentType,null)))
throw new ServletException("Content-Type != multipart/form-data");
return getParts(null);
}
@ -2405,7 +2411,7 @@ public class Request implements HttpServletRequest
private MultiParts newMultiParts(ServletInputStream inputStream, String contentType, MultipartConfigElement config, Object object) throws IOException
{
return new MultiParts.MultiPartsHttpParser(getInputStream(), getContentType(), config,
return new MultiParts.MultiPartsHttpParser(getInputStream(), contentType, config,
(_context != null ? (File) _context.getAttribute("javax.servlet.context.tempdir") : null), this);
}

View File

@ -30,7 +30,6 @@ import java.util.Locale;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
@ -1042,37 +1041,27 @@ public class Response implements HttpServletResponse
_reason = null;
_contentLength = -1;
List<HttpField> cookies = preserveCookies
?_fields.stream()
.filter(f->f.getHeader()==HttpHeader.SET_COOKIE)
.collect(Collectors.toList()):null;
List<HttpField> cookies = preserveCookies ?_fields.getFields(HttpHeader.SET_COOKIE):null;
_fields.clear();
String connection = _channel.getRequest().getHeader(HttpHeader.CONNECTION.asString());
if (connection != null)
for (String value: _channel.getRequest().getHttpFields().getCSV(HttpHeader.CONNECTION,false))
{
for (String value: StringUtil.csvSplit(null,connection,0,connection.length()))
HttpHeaderValue cb = HttpHeaderValue.CACHE.get(value);
if (cb != null)
{
HttpHeaderValue cb = HttpHeaderValue.CACHE.get(value);
if (cb != null)
switch (cb)
{
switch (cb)
{
case CLOSE:
_fields.put(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.toString());
break;
case KEEP_ALIVE:
if (HttpVersion.HTTP_1_0.is(_channel.getRequest().getProtocol()))
_fields.put(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.toString());
break;
case TE:
_fields.put(HttpHeader.CONNECTION, HttpHeaderValue.TE.toString());
break;
default:
}
case CLOSE:
_fields.put(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.toString());
break;
case KEEP_ALIVE:
if (HttpVersion.HTTP_1_0.is(_channel.getRequest().getProtocol()))
_fields.put(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.toString());
break;
case TE:
_fields.put(HttpHeader.CONNECTION, HttpHeaderValue.TE.toString());
break;
default:
}
}
}

View File

@ -32,7 +32,6 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;

View File

@ -0,0 +1,341 @@
//
// ========================================================================
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.server.jmh;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
@State(Scope.Benchmark)
@Threads(1)
@Warmup(iterations = 6, time = 2000, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 3, time = 2000, timeUnit = TimeUnit.MILLISECONDS)
public class ListVsMapBenchmark
{
@Param({ "12" }) // Chrome has 12 for HTTP/1.1 and 16 for HTTP/2 (including meta headers)
public static int size;
@Param({ "11" }) // average length of known headers in HttpHeader
public static int length;
@Param({"1", "10", "20", "30" })
public static int lookups;
@Param({"hits", "misses", "iterate" })
public static String mostly;
static final String base = "This-is-the-base-of-All-key-names-and-is-long".substring(0,length);
static final String miss = "X-" + base;
static final List<String> trials = new ArrayList<>();
static final Random random = new Random();
@Setup(Level.Trial)
public void setup()
{
int hits = 1;
int misses = 1;
switch(mostly)
{
case "hits" : hits = lookups; break;
case "misses" : misses = lookups; break;
case "iterate" : hits = lookups/2; misses=lookups-hits; break;
default : throw new IllegalStateException();
}
for (int h = hits; h-->0;)
trials.add(base + "-" + (h % size));
for (int m = misses; m-->0; )
trials.add(miss);
Collections.shuffle(trials);
}
static class Pair
{
final String key;
final String value;
public Pair(String key, String value)
{
this.key = key;
this.value = value;
}
}
interface Fill
{
void put(Pair p);
}
interface Lookup
{
Pair get(String key);
Iterator<Pair> iterate();
}
private void fill(Fill fill)
{
for (int i=0; i<size-1; i++)
{
String key = base + "-" + i;
Pair pair = new Pair(key, Long.toString(random.nextLong(),8));
fill.put(pair);
}
// double up on header 0
String key = base + "-0";
Pair pair = new Pair(key, Long.toString(random.nextLong(),8));
fill.put(pair);
}
private long test(Lookup lookup)
{
long result = 0;
if ("iterate".equals(mostly))
{
Iterator<String> t = trials.iterator();
while(t.hasNext())
{
// Look for 4 headers at once because that is what the common case of a
// ResourceService does
String one = t.hasNext() ? t.next() : null;
String two = t.hasNext() ? t.next() : null;
String three = t.hasNext() ? t.next() : null;
String four = t.hasNext() ? t.next() : null;
Iterator<Pair> i = lookup.iterate();
while (i.hasNext())
{
Pair p = i.next();
String k = p.key;
if (one != null && one.equals(k))
result ^= p.value.hashCode();
else if (two != null && two.equals(k))
result ^= p.value.hashCode();
else if (three != null && three.equals(k))
result ^= p.value.hashCode();
else if (four != null && four.equals(k))
result ^= p.value.hashCode();
}
}
}
else
{
for (String t : trials)
{
Pair p = lookup.get(t);
if (p != null)
result ^= p.value.hashCode();
}
}
return result;
}
private long listLookup(List<Pair> list)
{
return test(new Lookup() {
@Override
public Pair get(String k)
{
for (int i = 0; i<list.size(); i++ )
{
Pair p = list.get(i);
if (p.key.equalsIgnoreCase(k))
return p;
}
return null;
}
@Override
public Iterator<Pair> iterate()
{
return list.iterator();
}
});
}
@Benchmark
@BenchmarkMode({Mode.Throughput})
public long testArrayList() throws Exception
{
List<Pair> list = new ArrayList<>(size);
fill(list::add);
return listLookup(list);
}
@Benchmark
@BenchmarkMode({Mode.Throughput})
public long testLinkedList() throws Exception
{
List<Pair> list = new LinkedList<>();
fill(list::add);
return listLookup(list);
}
@Benchmark
@BenchmarkMode({Mode.Throughput})
public long testLinkedHashMap() throws Exception
{
// This loses the true ordering of fields
Map<String,List<Pair>> map = new LinkedHashMap<>(size);
fill(p->
{
List<Pair> list = new LinkedList<>();
list.add(p);
map.put(p.key.toLowerCase(),list);
});
return test(new Lookup()
{
@Override
public Pair get(String k)
{
List<Pair> list = map.get(k.toLowerCase());
if (list == null || list.isEmpty())
return null;
return list.get(0);
}
@Override
public Iterator<Pair> iterate()
{
Iterator<List<Pair>> iter = map.values().iterator();
return new Iterator<Pair>() {
Iterator<Pair> current;
@Override
public boolean hasNext()
{
if (( current==null || !current.hasNext() ) && iter.hasNext())
current=iter.next().iterator();
return current!=null && current.hasNext();
}
@Override
public Pair next()
{
if (hasNext())
return current.next();
throw new NoSuchElementException();
}
};
}
});
}
@Benchmark
@BenchmarkMode({Mode.Throughput})
public long testHashMapAndLinkedList() throws Exception
{
// This keeps the true ordering of fields
Map<String,List<Pair>> map = new HashMap<>(size);
List<Pair> order = new LinkedList<>();
fill(p->
{
List<Pair> list = new LinkedList<>();
list.add(p);
map.put(p.key.toLowerCase(),list);
order.add(p);
});
return mapLookup(map, order);
}
@Benchmark
@BenchmarkMode({Mode.Throughput})
public long testHashMapAndArrayList() throws Exception
{
// This keeps the true ordering of fields
Map<String,List<Pair>> map = new HashMap<>(size);
List<Pair> order = new ArrayList<>();
fill(p->
{
List<Pair> list = new ArrayList<>(2);
list.add(p);
map.put(p.key.toLowerCase(),list);
order.add(p);
});
return mapLookup(map, order);
}
private long mapLookup(Map<String, List<Pair>> map, List<Pair> order)
{
return test(new Lookup()
{
@Override
public Pair get(String k)
{
List<Pair> list = map.get(k.toLowerCase());
if (list == null || list.isEmpty())
return null;
return list.get(0);
}
@Override
public Iterator<Pair> iterate()
{
return order.iterator();
}
});
}
public static void main(String[] args) throws RunnerException
{
Options opt = new OptionsBuilder()
.include(ListVsMapBenchmark.class.getSimpleName())
// .addProfiler(GCProfiler.class)
.forks(1)
.build();
new Runner(opt).run();
}
}